Рубрики
Без рубрики

10 причин перейти с Java на Kotlin прямо сейчас!

Меня зовут Вячеслав Аксенов, я разработчик больших и сложных серверных систем. Я начал с Java… С тегом kotlin, java, программирование, jvm.

Меня зовут Вячеслав Аксенов, я разработчик больших и сложных серверных систем. Я начал с 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”