Отличия Kotlin от Java
Как и Java, Kotlin является статически типизированным языком. Он поддерживает и процедурное, и объектно-ориентированное программирование, а также имеет ряд преимуществ, о которых мы и поговорим.
Null-безопасность
При попытке возвращения либо присваивания null код не скомпилируется, то есть Kotlin не допустит возникновения NullPointerException и выдаст ошибку компиляции.
Тем не менее в языке существует поддержка 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
Классы данных (Data Classes)
В Kotlin есть специальные классы, которые предназначены специально для хранения данных. С их помощью можно генерировать разные шаблоны: hashCode(), equals(), toString(), сеттеры, геттеры и т. д. Посмотрим код на Джава:
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)
Кроме того, можно легко создавать копии классов данных посредством метода
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 заточен под функциональное программирование, предоставляя большое число полезных возможностей, допустим, лямбда-выражения, функции высшего порядка, перегрузку операторов, ленивые вычисления логических выражений.
Рассмотрим пример работы 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, берущую 2 числа, складывающую их и принимающую значение суммы, приведённое к целому. При этом для вызова достаточно простого sum(2,2).
Гибкий и простой синтаксис
Итак, простые функции и структуры мы можем объявлять одной строкой, геттеры и сеттеры задавать за кулисами для интероперабельности с Java-кодом, а добавление data-аннотации к классу активирует автогенерацию различных шаблонов. Результат — более гибкий и простой синтаксис.
Для сравнения приведём код на Java:
/* Программа на 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:
/* Та же программа на Kotlin */ data class Address(var street:String, var streetNumber:Int, var postCode:String, var city:String, var country:Country)
Источник: «Overview of Kotlin & Comparison With Java».