Меня зовут Вячеслав Аксенов, я разработчик больших и сложных серверных систем. Я начал с Java 7, затем перешел на Java 8, а иногда и на Java 11 для разработки. В прошлом году я написал весь новый код на Kotlin, и это полностью изменило мою жизнь. Раньше я не знал, что Kotlin можно использовать для разработки бэкэнда и более того – для разработки бэкэнда с использованием всех лучших фреймворков Java, таких как Spring, Jackson и т. Д.
Поэтому я хочу поделиться с вами своим счастьем и привести 10 причин, по которым вам следует ознакомиться с Kotlin и интегрировать его в свои проекты.
В Kotlin требуется явно указать, может ли конкретный метод возвращать значение null или нет. Таким образом, мы можем предположить, что все данные уже завернуты в аналог Optional. И исключение NullPointerException будет для вас настолько редким, что вы его пропустите.
fun saveReviewNullable(r: Review): Review? = reviewRepository.save(r) fun bar(r: Review) { val savedReviewNullable: Review = saveReviewNullable(r)!! // can throw NPE. Not safety val savedReviewNotNull: Review = saveReviewNullable(r) ?: Review() // safety way }
Суть в следующем: существует Первичный конструктор и Вторичный конструктор. Помощники должны вызывать основной в качестве конструктора родительского класса.
class Cat(val name: String, val color: String, val height: Int) { constructor(name: String) : this( name = name, color = "fixed color", height = 10 ) }
Пример избитый, но максимально понятный.
data class Cat(val name: String, val color: String, val height: Int)
И теперь то же самое происходит на Java:
public class Cat { private final String name; private final String color; private final Integer height; public Cat(String name, String color, Integer height) { this.name = name; this.color = color; this.height = height; } public String getName() { return name; } public String getColor() { return color; } public Integer getHeight() { return height; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Cat cat = (Cat) o; return Objects.equals(name, cat.name) && Objects.equals(color, cat.color) && Objects.equals(height, cat.height); } @Override public int hashCode() { return Objects.hash(name, color, height); } }
Мне кажется, что здесь даже комментарии излишни.
Указатели на классы данных в Kotlin по умолчанию подразумевают получение, установку (для полей var), равенство, хэш-код, строку для всех полей. Если вы хотите, вы можете переопределить каждый из этих методов по-своему, но это редко требуется.
class Cat(val name: String, val color: String, val height: Int) { override fun toString(): String = "overridden toString" }
Еще одно преимущество: в Kotlin вы получаете довольно простые и элегантные конструкции. Если вы хотите, чтобы поле to было изменяемым, то для его объявления используется var. Затем будет создан метод установки, и поле не будет окончательным.
И если вам нужно сделать поле неизменяемым, вы должны использовать val для объявления. Это выглядит очень красиво и просто. Плюс нет необходимости следить за вспомогательными методами.
Пример: поля цвета и высоты могут быть изменены после создания, а имя может быть изменено только при инициализации объекта:
data class Cat(val name: String, var color: String, var height: Int)
То, что появилось в Java чуть позже, уже давно есть в Kotlin – создание коллекций сразу становится неизменным.
val list = listOf("one", "two") val map = mapOf(1 to "one", 2 to "two") val set = setOf(1, 2 ,3)
Любые изменения в этих коллекциях создадут новую неизменяемую коллекцию после преобразования:
val list = listOf("one", "two") val list2 = list.plus("three")
Но вы не сможете изменить ни один элемент отдельно. Для классических изменяемых коллекций используются явно изменяемые аналоги:
val list = mutableListOf("one", "two") val map = mutableMapOf(1 to "one", 2 to "two") val set = mutableSetOf(1, 2 ,3)
Например, у вас действительно неудобная модель из другого API, которая хранит данные с действительно высокой вложенностью. В Java вам нужно было бы написать конвертер из одной бесполезной модели в полезную.
В Kotlin вы можете просто добавить расширение для любого класса, даже для класса из внешней библиотеки, и использовать его по своему усмотрению:
data class SomeBadModel( val innerModel: EvenBadModel ) data class EvenBadModel(tons of fields) fun SomeBadModel.doGoodStuff(val any: Any?): Any? = usefullLogicHere
Преимуществом Kotlin, которому я не перестаю радоваться, является возможность использовать операторы для базовых операций над сложными классами. Если вам нужно добавить десятичные числа, вы берете их и записываете через плюс. Вам не нужно явно вызывать метод в первом термине.
val a = BigDecimal(1) val b = BigDecimal(2) val sum = a + b
В Java вам нужно вызвать специальный метод:
BigDecimal a = new BigDecimal(1); BigDecimal b = new BigDecimal(2); BigDecimal sum = a.add(b);
То же самое и с массивами: если вы хотите удалить элемент из изменяемого массива, вы записываете массив за вычетом этого элемента. И если элемент присутствует, то он будет удален.
val list = listOf("one", "two") - "one" // list = ["two"]
Если метод прост и состоит из одной операции или цепочки операций, записанных в одной строке, то нет необходимости писать фигурные скобки и возвращать. Вы пишете прямо:
fun getReviewByTitle(title: String): List= reviewRepository.getAllByTitle(title)
Вместо опции Java:
public List(String title) { return reviewRepository.getAllByTitle(title); }
Интересные шаги в направлении функционального программирования в духе выделения контекста: лямбды можно поворачивать по своему усмотрению.
Существуют функции let, apply, а также функции with, run. Из-за их обилия сначала возникает вопрос: что подходит для конкретного случая. Но когда к этому привыкаешь, становится непонятно, как ты раньше жил без них.
Простой пример: возьмите результат и обработайте его каким-то образом:
fun save(review: Review): Review = repository.save(review) fun bar(r: Review) = saveReview(r).let { it.comment + it.date}
Или инициализировать объект и дополнительно инициализировать его поля var:
class Cat(val name: String, val height: Int) { var color: String? = null } fun bar() = Cat("Fred",10).apply { color = daoService.getPopularColor() }
Если вы просто смотрите на Kotlin для бэкэнда, то имейте в виду, что в среде, которая запускает ваш проект на Java 8, вы можете запустить скомпилированный проект в Kotlin без танцев с бубном. Да, на одной и той же jvm, в одной и той же среде и с минимальными усилиями.
Для меня было открытием, что даже в одном приложении могут быть классы на Java и Kotlin. Все волшебство происходит во время компиляции. В зависимости от настроек вы можете указать, что создавать в первую очередь: классы Kotlin или классы Java.
Теперь доступна компиляция исходных текстов Kotlin в байт-код из Java LTS – 8, 11 и (пока экспериментальный) 16.
Кто-то может сказать, что это сахарный сахар, и он будет прав. Но от себя скажу: если в язык включено большое количество шаблонных вещей, и вам не нужно постоянно думать о них, процесс разработки становится проще, количество ошибок меньше. Но вам нужно чаще думать о стиле кода, потому что без него вы можете многое испортить.
Сейчас я все еще продолжаю писать и видеть код на Java, но в 99% случаев это связано с образовательными программами, в которых я принимаю участие в качестве наставника.
Я советую всем попробовать это – хотя бы в домашнем проекте, чтобы понять, подходят ли вам парадигмы Kotlin или нет.
Оригинал: “https://dev.to/vaksenov/10-reasons-to-switch-from-java-to-kotlin-right-now-3ihj”