Привет, Хабр! Представляю вашему вниманию перевод статьи «Why you should totally switch to Kotlin» Magnus Vinther.
Я хочу рассказать вам о языке программирования под названием Kotlin и о том, почему вам стоит рассмотреть его в качестве языка для реализации вашего следующего проекта.
Раньше я писал код на Java, но в прошлом году я заметил, что всякий раз, когда мне по той или иной причине приходится писать код, я использую для этого Kotlin. Обнаружив это, я понял, что для себя не могу привести пример ситуации, когда Java лучше бы подошла для выполнения моих задач.
Kotlin является разработкой JetBrains — компании, разработавшей такие IDE, как IntelliJ и ReSharper. Kotlin насквозь пропитан профессионализмом разработчиков JetBrains — он является прагматичным и выразительным, что делает разработку на этом языке приятной и эффективной.
Хотя Kotlin можно компилировать в JavaScript и скоро будет возможность компилировать его в машинный код, я сфокусируюсь на основном его окружении — JVM.
Ниже я приведу несколько причин (не в порядке их значимости), по которым вам стоит попробовать перейти на Kotlin.
Kotlin полностью совместим с Java (любой код на Java будет работать для Kotlin, обратное — не обязательно). Таким образом, вы можете продолжить работу над своими существующими проектами на Java, используя Kotlin. Ваши любимые Java-фреймворки могут быть использованы вами в Kotlin, а работе с любым Kotlin-овским фреймворком можно без проблем научить любого вашего знакомого, программирующего на Java.
Kotlin не является непонятным нердо-академическим языком. Его синтаксис, скорее всего, покажется знакомым и понятным любому программисту, имеющему опыт работы с ООП. Конечно, у синтаксиса Kotlin есть некоторые отличия от Java, такие, как переработанные конструкторы, объявления
Интерполяция строк — встроенный в язык и более читаемый аналог
Kotlin умеет автоматически выводить типы в случаях, когда вы считаете, что это улучшит читаемость кода:
Компилятор Kotlin ориентируется на логику вашей программы и автоматически выполняет приведение типов, если это возможно Это означает, что вам больше не понадобится использовать
Вам больше не нужно явно вызывать
Использование аргументов по умолчанию позволяет не определять отдельно копию метода с меньшим количеством аргументов (например, при вызове метода, приведённого ниже, параметр
Именованные аргументы, вкупе с аргументами по умолчанию, позволяют устранить необходимость во множестве строителей:
Приведённый ниже код работает как выражение или оператор, с аргументом или без него:
Мы можем настроить поведение публичных полей классов удобным способом, избавившись от необходимости загромождения кода отдельными методами-геттерами и сеттерами.
С помощью конструкции
Для повышения читаемости кода можно перегружать стандартные операторы:
Некоторые объекты (например, структуры данных, такие как
Диапазоны могут быть использованы для удобного перебора чисел
Вспомните, как вы в первый раз решили отсортировать
Было бы здорово иметь возможность добавлять собственные методы к уже существующим классам так, чтобы о них потом могла напомнить IDE во время автодополнения кода. В Kotlin есть возможность так делать:
Стандартная библиотека таким же образом расширяет функциональность старых классов из Java, например для
Мы можем назвать язык Java почти статически типизированным. Код на Java не гарантирует, что переменная типа
В Kotlin эта проблема решается разделением типов на non-null (типы, значением которых не может быть null) и nullable (типы, значением которых может быть null). По умолчанию типы является non-null и могут сделаны nullable посредством добавления
Kotlin заставляет вас обязательно проверять на null значения nullable типов:
Эта особенность языка может показаться громоздкой, но, благодаря умным преобразованиям типов, работать с ней оказывается легко и удобно:
Мы также можем воспользоваться безопасным вызовом метода с помощью
null-безопасные вызовы методов могут быть объединены в цепочки во избежание вложенных проверок на null, иногда необходимых в других языках программирования. Если в качестве значения по умолчанию мы хотим получить не null, а какое-то иное, мы можем настроить это с помощью оператора
Если всё вышеперечисленное вам не подходит и вы всё же хотите получать Null-Pointer Exception, вам нужно явно это указать:
Система лямбда-функций в Kotlin отлично сбалансирована между читаемостью и лаконичностью благодаря удачным архитектурным решениям. Синтаксис лямбда-функций выглядит следующим образом:
Несколько хитростей:
Таким образом, следующие строки кода эквивалентны:
И мы получаем возможность писать выразительный функциональный код — просто взгляните на него!
Система лямбда-функций в Kotlin вместе с функциями-расширениями делают его идеальным для создания DSL (Domain-Specific Languages). anko является примером DSL, созданного с целью упрощения Android-разработки:
У вас есть несколько вариантов, если вы решить попробовать Kotlin, но я рекомендую попробовать IntelliJ — на её примере вы сможете увидеть преимущества разработки языка и IDE для него одними и теми же людьми.
Когда я впервые попробовал скопировать код на Java со Stack Overflow, я получил следующее сообщение от IDE:
На этом я и закончу. Ссылка на официальный сайт Kotlin.
Я хочу рассказать вам о языке программирования под названием Kotlin и о том, почему вам стоит рассмотреть его в качестве языка для реализации вашего следующего проекта.
Раньше я писал код на Java, но в прошлом году я заметил, что всякий раз, когда мне по той или иной причине приходится писать код, я использую для этого Kotlin. Обнаружив это, я понял, что для себя не могу привести пример ситуации, когда Java лучше бы подошла для выполнения моих задач.
Kotlin является разработкой JetBrains — компании, разработавшей такие IDE, как IntelliJ и ReSharper. Kotlin насквозь пропитан профессионализмом разработчиков JetBrains — он является прагматичным и выразительным, что делает разработку на этом языке приятной и эффективной.
Хотя Kotlin можно компилировать в JavaScript и скоро будет возможность компилировать его в машинный код, я сфокусируюсь на основном его окружении — JVM.
Ниже я приведу несколько причин (не в порядке их значимости), по которым вам стоит попробовать перейти на Kotlin.
0. Совместимость с Java
Kotlin полностью совместим с Java (любой код на Java будет работать для Kotlin, обратное — не обязательно). Таким образом, вы можете продолжить работу над своими существующими проектами на Java, используя Kotlin. Ваши любимые Java-фреймворки могут быть использованы вами в Kotlin, а работе с любым Kotlin-овским фреймворком можно без проблем научить любого вашего знакомого, программирующего на Java.
1. Знакомый синтаксис
Kotlin не является непонятным нердо-академическим языком. Его синтаксис, скорее всего, покажется знакомым и понятным любому программисту, имеющему опыт работы с ООП. Конечно, у синтаксиса Kotlin есть некоторые отличия от Java, такие, как переработанные конструкторы, объявления
var
и val
, синтаксис для описания типов параметров функций и некоторые другие. Некоторые базовые нововведения представлены на следующем фрагменте кода:class Foo {
val b: String = "b" // val запрещает изменять значения переменной
var i: Int = 0 // var разрешает изменять значение переменной
fun hello() {
val str = "Hello"
print("$str World")
}
fun sum(x: Int, y: Int): Int { //типы параметров функции и возвращаемого значения пишутся через двоеточие
return x + y
}
fun maxOf(a: Float, b: Float) = if (a > b) a else b
}
2. Интерполяция строк
Интерполяция строк — встроенный в язык и более читаемый аналог
String.format()
из Java:val x = 4
val y = 7
print("$x + $y = ${x + y}") // будет выведено: "4 + 7 = 11"
3. Вывод типов
Kotlin умеет автоматически выводить типы в случаях, когда вы считаете, что это улучшит читаемость кода:
val a = "abc" // будет выведен тип String
val b = 4 // будет выведен тип Int
val c: Double = 0.7 // явное объявление типа
val d: List<String> = ArrayList() // явное объявление типа
4. Умное приведение типов
Компилятор Kotlin ориентируется на логику вашей программы и автоматически выполняет приведение типов, если это возможно Это означает, что вам больше не понадобится использовать
instanceof
вместе с явным приведением типа — если проверка прошла успешно, приведение будет выполнено неявно.if (obj is String) {
print(obj.toUpperCase()) // в этом месте компилятору известно, что obj должен быть приведён к типу String
}
5. Интуитивно-понятные сравнения
Вам больше не нужно явно вызывать
equals()
, потому что с помощью оператора ==
теперь можно проверять структурное равенство объектов:val john1 = Person("John")
val john2 = Person("John")
john1 == john2 // true (структурное равенство)
john1 === john2 // false (равенство ссылок)
6. Аргументы по умолчанию
Использование аргументов по умолчанию позволяет не определять отдельно копию метода с меньшим количеством аргументов (например, при вызове метода, приведённого ниже, параметр
width
можно опускать):fun build(title: String, width: Int = 800, height: Int = 600) {
Frame(title, width, height)
}
7. Именованные аргументы
Именованные аргументы, вкупе с аргументами по умолчанию, позволяют устранить необходимость во множестве строителей:
build("PacMan", 400, 300) // эквивалентен двум другим объектам
build(title = "PacMan", width = 400, height = 300) // эквивалентен двум другим объектам
build(width = 400, height = 300, title = "PacMan") // эквивалентен двум другим объектам
8. Выражение when
switch
заменён гораздо более читаемым и гибким выражением when
:when (x) {
1 -> print("x is 1")
2 -> print("x is 2")
3, 4 -> print("x is 3 or 4")
in 5..10 -> print("x is 5, 6, 7, 8, 9, or 10")
else -> print("x is out of range")
}
Приведённый ниже код работает как выражение или оператор, с аргументом или без него:
val res: Boolean = when {
obj == null -> false
obj is String -> true
else -> throw IllegalStateException()
}
9. Свойства
Мы можем настроить поведение публичных полей классов удобным способом, избавившись от необходимости загромождения кода отдельными методами-геттерами и сеттерами.
class Frame {
var width: Int = 800
var height: Int = 600
val pixels: Int
get() = width * height
}
9. Data Class
С помощью конструкции
data class
можно легко создать класс, являющийся POJO, реализующий методы toString()
, equals()
, hashCode()
иcopy()
без необходимости писать для этого десятки строк кода:data class Person(val name: String,
var email: String,
var age: Int)
val john = Person("John", "john@gmail.com", 112)
11. Перегрузка операторов
Для повышения читаемости кода можно перегружать стандартные операторы:
data class Vec(val x: Float, val y: Float) {
operator fun plus(v: Vec) = Vec(x + v.x, y + v.y)
}
val v = Vec(2f, 3f) + Vec(4f, 1f)
12. Деструктурирующее присваивание
Некоторые объекты (например, структуры данных, такие как
map
) могут быть деструктурированы для удобного прохода по ним:for ((key, value) in map) {
print("Key: $key")
print("Value: $value")
}
13. Диапазоны
Диапазоны могут быть использованы для удобного перебора чисел
for (i in 1..100) { ... }
for (i in 0 until 100) { ... }
for (i in 2..10 step 2) { ... }
for (i in 10 downTo 1) { ... }
if (x in 1..10) { ... }
14. Функции-расширения
Вспомните, как вы в первый раз решили отсортировать
List
в Java. Вы, вероятно, не смогли найти функцию sort()
и были вынуждены выяснить у преподавателя или Google о методе Collections.sort()
. Позднее, когда вам понадобилось преобразовать строку к верхнему регистру, вам, возможно, пришлось писать собственную вспомогательную функцию из-за незнания о StringUtils.capitalize()
.Было бы здорово иметь возможность добавлять собственные методы к уже существующим классам так, чтобы о них потом могла напомнить IDE во время автодополнения кода. В Kotlin есть возможность так делать:
fun String.replaceSpaces(): String {
return this.replace(' ', '_')
}
val formatted = str.replaceSpaces()
Стандартная библиотека таким же образом расширяет функциональность старых классов из Java, например для
String
были добавлены следующие методы:str.removeSuffix(".txt");
str.capitalize();
str.substringAfterLast("/");
str.replaceAfter(":", "classified");
15. Null-безопасность
Мы можем назвать язык Java почти статически типизированным. Код на Java не гарантирует, что переменная типа
String
содержит именно строку, а не null
. Хотя мы и привыкли к этому, это несколько снижает безопасность, обеспечиваемую статической типизацией, в результате чего разработчики на Java должны регулярно думать о возможных Null-Pointer Exceptions.В Kotlin эта проблема решается разделением типов на non-null (типы, значением которых не может быть null) и nullable (типы, значением которых может быть null). По умолчанию типы является non-null и могут сделаны nullable посредством добавления
?
к типу:var a: String = "abc"
a = null // ошибка компиляции
var b: String? = "xyz"
b = null // нет ошибок
Kotlin заставляет вас обязательно проверять на null значения nullable типов:
val x = b.length //ошибка компиляции: b может быть null
Эта особенность языка может показаться громоздкой, но, благодаря умным преобразованиям типов, работать с ней оказывается легко и удобно:
if (b == null) return
val x = b.length //ошибки не будет
Мы также можем воспользоваться безопасным вызовом метода с помощью
?.
, который, если значение будет null, вернёт нам null вместо выбрасывания ошибки:val x = b?.length // тип x - nullable Int
null-безопасные вызовы методов могут быть объединены в цепочки во избежание вложенных проверок на null, иногда необходимых в других языках программирования. Если в качестве значения по умолчанию мы хотим получить не null, а какое-то иное, мы можем настроить это с помощью оператора
?:
:val name = ship?.captain?.name ?: "unknown"
Если всё вышеперечисленное вам не подходит и вы всё же хотите получать Null-Pointer Exception, вам нужно явно это указать:
val x = b?.length ?: throw NullPointerException() // результат как у выражения ниже
val x = b!!.length // результат как у выражения выше
16. Улучшенные лямбда-функции
Система лямбда-функций в Kotlin отлично сбалансирована между читаемостью и лаконичностью благодаря удачным архитектурным решениям. Синтаксис лямбда-функций выглядит следующим образом:
val sum = { x: Int, y: Int -> x + y } // тип: (Int, Int) -> Int
val res = sum(4,7) // res == 11
Несколько хитростей:
- Внешние скобки могут быть опущены если лямбда-функция является последним или единственным аргументом другой функции.
- Мы можем не объявлять явно единственный аргумент лямбда-функции — тогда он будет неявно объявлен под именем
it
.
Таким образом, следующие строки кода эквивалентны:
numbers.filter({ x -> x.isPrime() })
numbers.filter { x -> x.isPrime() }
numbers.filter { it.isPrime() }
И мы получаем возможность писать выразительный функциональный код — просто взгляните на него!
persons
.filter { it.age >= 18 }
.sortedBy { it.name }
.map { it.email }
.forEach { print(it) }
Система лямбда-функций в Kotlin вместе с функциями-расширениями делают его идеальным для создания DSL (Domain-Specific Languages). anko является примером DSL, созданного с целью упрощения Android-разработки:
verticalLayout {
padding = dip(30)
editText {
hint = “Name”
textSize = 24f
}
editText {
hint = “Password”
textSize = 24f
}
button(“Login”) {
textSize = 26f
}
}
17. Поддержка IDE
У вас есть несколько вариантов, если вы решить попробовать Kotlin, но я рекомендую попробовать IntelliJ — на её примере вы сможете увидеть преимущества разработки языка и IDE для него одними и теми же людьми.
Когда я впервые попробовал скопировать код на Java со Stack Overflow, я получил следующее сообщение от IDE:
На этом я и закончу. Ссылка на официальный сайт Kotlin.