Kotlin — это относительно молодой язык от российской компании JetBrains. Появился он в 2011 году. На конференции Google I/O 2017 команда разработчиков Android сообщила, что Kotlin получил официальную поддержку для разработки Android-приложений.
Как и Java, C и C++, Kotlin — это статически типизированный язык. Он поддерживает как объектно-ориентированное, так и процедурное программирование. По аналогии с вышеупомянутыми языками, основной код Kotlin-программы пишется в функции main
, которой передаётся массив аргументов командной строки:
// необязательный заголовочный файл пакета
package hello
fun main(args: Array < String > ) {
val scope = "world"
println("Hello, $scope!") //точки с запятыми необязательны
}
Вот основные возможности и преимущества Kotlin:
- компилируется в байткод JVM или в JavaScript;
- программы могут использовать все существующие Java-фреймворки и библиотеки. Kotlin можно интегрировать с Maven, Gradle и другими системами сборки;
- язык очень прост для изучения;
- исходный код открыт;
- в IntelliJ доступна автоматическая конвертация Java-кода в Kotlin и наоборот;
- язык null-безопасен — надоедливые NullPointerException остались в Java. Вот пример кода:
val name: String = null // попытка присвоить null, не скомпилируется. fun getName() : String = null // попытка вернуть null, не скомпилируется.
- легко читаемый синтаксис не составит проблем при code review.
Разберём некоторые из них поподробнее.
Null-безопасность
При попытке присваивания или возвращения null код не скомпилируется. Тем не менее, в языке есть поддержка Nullable-типов. Задать такую переменную или функцию можно, приписав ?
к названию типа:
val name: String? = null // присваивается null, код компилируется.
fun getName() : String? = null // возвращается null, код компилируется.
/* неверно */
val name: String? = null
val len = name.length
/* верно */
val name: String? = null
val len = name?.length
Гибкость и простота синтаксиса
Простые функции и структуры можно объявить одной строкой. Геттеры и сеттеры задаются за кулисами для интероперабельности с Java-кодом. Добавление data-аннотации к классу активирует автоматическую генерацию различных шаблонов.
Рассмотрим следующий пример:
/* Программа на Java */
public class Address {
private String street;
private int streetNumber;
private String postCode;
private String city;
private Country country;
public Address(String street, int streetNumber, String postCode, String city, Country country) {
this.street = street;
this.streetNumber = streetNumber;
this.postCode = postCode;
this.city = city;
this.country = country;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
if (streetNumber != address.streetNumber) return false;
if (!street.equals(address.street)) return false;
if (!postCode.equals(address.postCode)) return false;
if (!city.equals(address.city)) return false;
return country == address.country;
}
@Override
public int hashCode() {
int result = street.hashCode();
result = 31 * result + streetNumber;
result = 31 * result + postCode.hashCode();
result = 31 * result + city.hashCode();
result = 31 * result + (country != null ? country.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", streetNumber=" + streetNumber +
", postCode='" + postCode + '\'' +
", city='" + city + '\'' +
", country=" + country +
'}';
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public int getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(int streetNumber) {
this.streetNumber = streetNumber;
}
public String getPostCode() {
return postCode;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
}
/* Та же программа на Kotlin */
data class Address(var street:String,
var streetNumber:Int,
var postCode:String,
var city:String,
var country:Country)
Отличия от Java
Null-безопасность
Как уже говорилось ранее, Kotlin не допускает возникновения NullPointerException, выдавая ошибку компиляции.
Классы данных (Data Classes)
В Kotlin появились специальные классы, предназначенные специально для хранения данных. Они генерируют различные шаблоны: equals()
, hashCode()
, toString()
, геттеры и сеттеры и т.д. Сравните код на Java:
class Book {
private String title;
private Author author;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
И на Kotlin:
/* Kotlin */
data class Book(var title:String,var author:Author)
Легко создавать копии классов данных при помощи метода copy()
:
val book = Book("Kotlin", "JetBrains")
val copy = book.copy()
Функции-расширения
Kotlin позволяет расширять функциональность существующих классов, не прибегая к наследованию. Это делается при помощи функций-расширений. Для объявления такой функции к её имени нужно приписать префикс в виде расширяемого типа. Вот так можно добавить функцию swap
в MutableList
:
fun MutableList<Int>.swap(index1:Int,index2:Int){
val tmp=this[index1]
this[index1]=this[index2]
this[index2]=tmp
}
Ключевое слово this внутри функции-расширения относится к объекту-получателю, который передаётся перед точкой. Теперь мы можем применить функцию swap к любому изменяемому списку:
val abc = mutableListOf(1, 2, 3)
abc.swap(0, 2)
Умные приведения типов
Компилятор Kotlin очень умён, когда речь заходит о приведениях типов. В большинстве случаев не требуется явно указывать операторы приведения, поскольку в языке есть оператор is
, который делает за вас всю работу:
fun demo(x:Any) {
if(x is String) {
print(x.length) // x автоматически приводится к типу String
}
}
Вывод типов
В Kotlin необязательно явно указывать тип переменной:
/* неявное определение */
fun main(args: Array<String>) {
val text = 10
println(text)
}
/* явное определение */
fun main(args: Array<String>) {
val text: Int = 10
println(text)
}
Функциональное программирование
Важно отметить, что Kotlin заточен под функциональное программирование. Он предоставляет большое количество полезных возможностей, например, функции высшего порядка, лямбда-выражения, перегрузку операторов и ленивые вычисление логических выражений. Вот пример работы с коллекциями:
fun main(args: Array<String>) {
val numbers = arrayListOf(15, -5, 11, -39)
val nonNegativeNumbers = numbers.filter { it >= 0 }
println(nonNegativeNumbers)
}
// Вывод: 15, 11
Функции высшего порядка — это функции, которые принимают другие функции в качестве аргументов и возвращают функции. Рассмотрим следующий пример:
fun alphaNum(func: () -> Unit) {}
В нём func
— это имя аргумента, а ( ) -> Unit
— это тип функции. Мы говорим, что func
будет функцией, не принимающей аргументов и ничего не возвращающей.
Лямбда-выражения, или анонимные функции — это функции, которые не объявляются, а передаются в виде выражений. Вот пример:
val sum: (Int, Int) -> Int = { x, y -> x + y }
Мы объявляем переменную sum
, которая берёт два числа, складывает их и принимает значение суммы, приведённое к целому. Для вызова достаточно простого sum(2,2)
.
Сравнение скорости Java и Kotlin
Первая сборка Kotlin-кода занимает примерно на 15–20% больше времени, чем аналогичный процесс на Java. Однако инкрементная сборка Kotlin даже немного быстрее, чем у Java. Таким образом, языки примерно равны по скорости компиляции.
Будущее Kotlin
Kotlin — это следующий этап развития Java, с которой он полностью совместим. Это делает его отличным инструментом для мобильных и энтерпрайз-приложений. А поскольку Kotlin теперь является официальным языком Android, можно не бояться, что, изучив его, вы останетесь без работы.
В изучении этого языка вам поможет серия статей, описывающая процесс создания простого мобильного приложения Keddit — клиента для популярного ресурса Reddit. Все ее части для вашего удобства мы перечислили в списке ниже.
Вам потребуются следующие библиотеки:
- Retrofit 2.0;
- RxJava;
- Picasso;
- RecyclerView;
- Расширения Kotlin для Android;
- Dagger 2.
Все исходники доступны на GitHub. Серия состоит из следующих частей:
- Настройка Android Studio.
- MainActivity.kt: синтаксис, null-безопасность и другое.
- NewsFragment.kt: функции-расширения, Android-расширения и т.д.
- RecyclerView : классы данных и адаптеры делегатов.
- Kotlin, RxJava и RxAndroid.
- API : Retrofit и Kotlin.
- Бесконечная прокрутка: функции высшего порядка и лямбды.
- Изменение ориентации экрана.
- Юнит-тестирование: Mockito, RxJava и Spek.
- Внедрение зависимостей: Kotlin и Dagger 2.
- Непрерывная интеграция: BuddyBuild.
- Заключение.
Если вам удобнее видеоформат, обратите внимание на русскоязычный видеокурс по Kotlin.
Адаптированный перевод статьи «Overview of Kotlin & Comparison With Java»