(java || kotlin) && devOps
369 subscribers
6 photos
1 video
6 files
306 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

Еще немного про Maven. Есть несколько способов настроить версию Java в Maven, все они описаны в этой статье - https://www.baeldung.com/maven-java-version
Оптимальным начиная с Java 9 выглядит вариант с указанием
<properties>
<maven.compiler.release>9</maven.compiler.release>
</properties>
т.к. данное свойство заменяет собой три: source, target и bootclasspath. Последнее означает, что начиная с Java 9 все JDK умеют притворяться более ранними версиями с точностью до classpath, т.е. до предоставляемого API. А это важно при переходе на 11-ю и 17-ю Java - в первой выпилили Java EE модули, типа SOAP, во второй - закрыли internal пакеты.

Но это не все, что нужно знать про настройки версии JDK.
Если в проекте есть Kotlin - надежнее явно указать ему версию JDK. Для справки - старые версии Kotlin поддерживали только JDK 6 как target bytecode, потом был период JDK 8, сейчас поддерживается все версии https://kotlinlang.org/docs/maven.html
Настраивается так:
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<jvmTarget>1.9</jvmTarget>
По хорошему плагин Kotlin должен переиспользовать maven.compiler.release, но я не нашел подтверждающей это информации.

Второй тонкий момент связан с IDEA. IDEA смотрит на maven.compiler.release, в результате правильно проставляет:
а) Source language level для корневого и дочерних модулей Maven - это можно увидеть в Project Structure -> Modules
б) Target bytecode для всех модулей - Double Shift, далее вбиваем "java compiler".
Но есть еще одна важная опция Project Structure -> Project -> SDK. Она определяет версию JDK, на которой будет собираться проект. Повторюсь, из-за сильных изменений в структуре JDK как минимум в 11 и 17 версиях сборка на более новой JDK может сломаться. Причем из логов причина ошибки будет не ясна(
Так вот, IDEA в данном кейсе игнорирует настройки Maven. Прямого решения у проблемы я не нашел, судя по всему, ребята из IntelliJ считают, что корневой модуль в проекте Maven и проект IDEA это не одно и тоже. И это фича, а не баг)
Но есть обходной путь. Если все или большинство ваших проектов живут на определенной версии JDK, то ее можно выставить в IDEA по умолчанию. Настраивается вот здесь: Double Shift, далее вбиваем "structure for new projects"

#maven #java #kotlin
Всем привет!

В последние годы стала "модной" тема null safety. Суть в том, что не нужно хранить и передавать null значения, чтобы не напороться на Null Pointer Exception. В том же Kotlin null safety встроена в язык - все типы по умолчанию не могут содержать null.
И на самом деле это правильный подход. Но есть нюансы)

Рассмотрим такой случай - мы идем куда-то за данными, данные по бизнес-процессу там обязаны быть. Например, мы прихранили id записи где-то в пользовательском контексте в начале процесса и идем за данными в конце процесса. Но данных нет. Следуя null safety можно просто создать пустой объект - например, с помощью конструктора. Как вариант, часть полей этого объекта будет проинициализирована значениями по умолчанию.

Так вот - в случае, когда данных нет из-за какой-то нештатной редко воспроизводимой ситуации: неверные тестовые данные, на сервис идет атака с перебором всех возможных значений, в процессе операции данные некорректно мигрировали, кривая архитектура - лучше просто "упасть", т.е. выбросить исключение. Есть такой принцип - fail fast. Т.к. создавая пустой объект, мы во-первых надеемся что он будет корректно обработан выше, а это может быть не так. А во-вторых - а зачем передавать управление дальше?

P.S. Как всегда - напомню каждую ситуацию нужно рассматривать индивидуально, чтобы различать отсутствие данных как часть бизнес-процесса и нештатную ситуацию.

#kotlin #code #patterns #principles #nullsafety #fail_fast
Всем привет!

Есть такая отличная штука в Kotlin, как контекстные функции.
Вот документация https://kotlinlang.org/docs/scope-functions.html
Вот пример, хорошо иллюстрирующий зачем они нужны:

val man = Person("Vasya").apply {
age = 20 // same as this.age = 20
city = "Moscow"
}

Код стал проще, читается хорошо. Если, конечно, ты знаешь про контекстные функции)

Но как и любая вещь, контекстные функции могут быть использованы не только во благо(
Вот несколько антипаттернов:

val params = claim.systemState?.let {
FailureParams(
claim.partnerName,
it.name,
)
}

Что мне здесь не нравится - читаемость. Если читать сверху вниз, то получается, что мы берем статус из заявки и присваиваем переменной params ... не его, а совершенно другой объект, созданный внутри let. Скорость понимания кода страдает.

return claim.also {
saveToCache(it)
}

Опять же, мне не нравится читаемость кода. Мы возвращаем результат метода, claim. А нет, не возвращаем. Вначале пишем его в кэш. А потом уже возвращаем.
Гораздо проще было бы:

saveToCache(claim)
return claim

Ну и наконец самый хит:

return claim.also {
saveToCache(someOtherObject)
}

Зачем? Почему? Не понятно)

P.S. По ссылке выше есть неплохая табличка https://kotlinlang.org/docs/scope-functions.html#function-selection Это по сути навигатор по контекстным функциям - позволяет выбрать одну из 6 для вашего конкретного случая. На первое время точно будет полезной

#kotlin #antipatterns
Всем привет!

Сегодня пост о крутой фиче Kotlin, которая решила одну важную проблему. И добавила другую)
Я о Null safety.
Суть ее в том, что в Kotlin любой тип представлен в двух ипостасях - одна может содержать null значения, другая - нет.
String - не может содержать null,
String? - может.
Это два разных типа, неявное приведение второго к первому "без приседаний" невозможно.
Что дает Null safety?
По умолчанию предлагается использовать тип not null, и если так и делать, то кажется, что про NPE - NullPointerException - можно забыть. А заодно забыть о проверках на null как в коде, так и в тестах. Небольшой оффтоп - проверка not null значений на null в тестах - еще один антипаттерн. Говорит о том, что пишущий этот код еще не до конца познал Kotlin)
Вроде бы все хорошо. Но есть нюанс. Что будет, если присвоить not null переменной nullable значение? Ошибка компиляции. А всегда ли компилятор знает, что это nullable значение? Если тип Kotlin - то всегда. Если тип Java - то в общем случае не знает. Что же он делает в таком случае? А ничего, просто разрешает присваивание.

//Java
public class JavaNullability {
private Boolean value;

public Boolean getValue() {
return value;
}
}

// Kotlin
class KotlinNullability {
constructor(test: JavaNullability) {
val nullValue: Boolean? = test.value
val notNullValue: Boolean = test.value
// компилятор разрешает оба варианта
}
}

Что же будет в runtime? Привычный нам в Java NPE на втором присваивании. Да, null safety не защищает от NPE в Kotlin коде.

Что тут можно сделать?

1) при взаимодействии Kotlin-Java знать nullability Java параметров, с которым мы работаем. Это может быть описание контракта - OpenAPI спецификация или любой другой контракт.
2) явная проверка на null в коде присваивания
3) самый плохой вариант, антипаттерн - всегда присваивать значения из Java nullable типам в Kotlin. Почему это плохо? nullable типы расползаются по всей программе, и в итоге мы теряем все преимущества Kotlin null safety.

P.S. Но в целом: null safety - это круто! Подробнее можно о ней можно почитать в официальной документации: https://kotlinlang.org/docs/null-safety.html#nullable-receiver

#kotlin #java #antipatterns #nullsafety
Всем привет!

Чтобы проиллюстрировать предыдущий пост - предлагаю сравнить по критерию null safety три языка: Groovy, Java и Kotlin.
Тестовая задача такая - передать null Boolean значение в конструктор и проверить его в if.

Groovy

import groovy.transform.TupleConstructor

@TupleConstructor
class GroovyNullability {
Boolean value

void checkValue() {
if (!value) {
println 'groovy value is false'
}
}
}

Т.к. в Groovy все, что не true считается false - вызов checkValue распечатает строку.

Java

public class JavaNullability {
private Boolean value;

public JavaNullability(Boolean value) {
this.value = value;
}

public void checkValue() {
if (!value) {
System.out.println("java value is false");
}
}
}


Вызов checkValue упадет c NPE: "Cannot invoke "java.lang.Boolean.booleanValue()" because "this.value" is null" на преобразовании Boolean к boolean использования в логическом выражении (boolean expression).

Kotlin

class KotlinNullability {
var value: Boolean = false

constructor(test: Boolean?) {
value = test!!
}

fun checkValue() {
if (!value) {
println("kotlin value is false")
}
}
}

Чтобы присвоить null значение not null переменной придется пойти на хитрость - через !! указать, что мы уверены, что в nullable переменной не будет null значений. И если же null все же придет - тоже будет java.lang.NullPointerException, причем без подробностей. Но на шаг раньше, еще на присваивании not null переменной.
К слову - если передать Java объект с null значением, через класс-обвертку:

constructor(test: JavaNullability) {
value = test.value
}

NPE будет чуть более подробным: "java.lang.NullPointerException: test.value must not be null"

#kotlin #java #groovy #nullsafety
Резюмирую предыдущий пост:

1) в Groovy нет null safety
2) в Java проблемы с null проявятся при попытке работы с null объектом, в т.ч. при неявных преобразованиях
3) в Kotlin с использованием Java библиотек проблемы будут проявляться при присваивании в Kotlin коде
4) чистый Kotlin = null safety

#groovy #kotlin #java #nullsafety
Всем привет!

Я уже писал о проблеме интероперабельности Kotlin-Java касающейся null safety - https://t.me/javaKotlinDevOps/190
Есть и еще одна, коллега столкнулся с ней недавно.
В Kotlin четко разделяет изменяемые и неизменяемые коллекции на уровне типов, по умолчанию предлагая делать их неизменяемыми.
В Java наоборот - по умолчанию все коллекции изменяемые, хотя неизменяемые создать тоже можно, например, Collections.unmodifiableXXX, но это не единственный способ.
Итого: предположу, что в Kotlin условно 90% коллекций немодифицируемые, в Java - наоборот 90% модифицируемые.

Что же будет при передаче коллекции из Kotlin в Java?
Да, хорошая практика независимо от языка - не менять передаваемые в метод параметры. Но эта практика не стимулируется к использованию Java.
Да, иммутабельность можно проверить, но делается это довольно криво и я подозреваю делается редко: https://ru.stackoverflow.com/questions/608545/collections-unmodifiable-как-определить
Отсюда получаем, что с большой вероятностью возможна ошибка UnsupportedOperationException на вставке в эти коллекции в Java.

#java #kotlin
Всем привет!

Проверяемые исключения - еще одна вещь, которой отличаются Java и Kotlin.
В Kotlin их нет, и вот тут описано почему https://kotlinlang.ru/docs/reference/exceptions.html
Там же есть ссылка на диалог таких известных людей как Bruce Eckel и Anders Hejlsberg на эту тему https://www.artima.com/articles/the-trouble-with-checked-exceptions
Вот тут есть его русский перевод https://habr.com/ru/articles/221723/

В целом соглашусь с основным аргументом авторов: с ростом кодовой базы - а тут важно помнить, что кроме кода сервиса мы еще используем кучу библиотек включая вездесущий Spring Framework - проверяемых исключений становится слишком много. А их выборочная обработка в бизнес приложении, которое активно использует внешние библиотеки, часто не нужна. Т.е. есть цепочка вызова из к примеру 10 сервисов, а обработка исключений - в одном из них, максимум в двух. В остальных случаях их приходится пробрасывать. Отсюда приходим к throws Exception. А это явный антипаттерн.
Следовательно, в большинстве случаев вред от проверяемых исключений перевешивает пользу. Хотя идея - если рассматривать ее именно как идею - красивая: объявляем в API не только типы входящих и исходящих параметров, но и потенциально возможные ошибки. Вот только удобной реализации пока никто не придумал)))

#java #kotlin #exceptions #checked_exceptions
Всем привет!

Что включает в себя понятие объекта?
1) объект может иметь данные
2) данные объекта можно менять
3) объект может иметь поведение - методы
4) от класса объекта можно наследоваться
5) объектов одного класса может быть несколько
6) каждый объект уникален - находится в heap по своему адресу. Т.е. имеет identity

Можно ли отобрать у объекта что-то из вышеперечисленного в Java?

1) final class - запрещаем наследование
2) sealed class - если полный запрет нам не подходит, то ограничиваем наследование https://habr.com/ru/companies/otus/articles/720044/
3) final поля - запрещаем менять данные класса после создания объекта - иммутабельность
4) records - запрещаем методы и изменение полей https://habr.com/ru/articles/487308/ Основое применение - DTO, где есть только данные и их нежелательно менять.
5) lambda - можно рассматривать как объект-функцию без данных, хотя технически это не так - JVM под капотом транслирует ее в статический метод https://javadungeon.wordpress.com/2017/08/02/java-8-lambdas-under-the-hood/
6) object в Kotlin - синлетона на уровне языка в Java нет, но такую конструкцию сделали в Kotlin
7) value class - еще не реализованная в Java концепция, которая по сути добавляет в язык что-то среднее между примитивным типом и объектом. Т.е. у нас будет объект, но в плане занимаемой памяти он будет почти также эффективен, как и примитив. Что важно: т.к. это объект - его можно будет использовать в generics. Ура-ура!. Детали: https://www.baeldung.com/java-valhalla-project Но ничего не бывает бесплатно - у такого объекта не будет identity, т.е. два объекта с одинаковыми полями считаются одинаковыми. Также, как это сейчас происходит с примитивными типами. Да, для объектов это уже сейчас можно реализовать на уровне кода. А тут идея сделать тоже самое на уровне JVM. Т.е. JVM может (обязана?) оставить один экземпляр для всех одинаковых объектов. Причем так, как это сделано сейчас со строками - вариант не очень, т.к. остается "дыра" с конструктором, который по определению всегда создает новый объект. Т.е. для value class нельзя использовать публичный конструктор, нужен какой-то фабричный метод - как элемент языка. Примеры, когда это полезно: те же классы-обвертки для чисел или географические объекты.

#java #oop #kotlin
Всем привет!

Продолжая тему Maven. Когда я изучал документацию по Maven, то наткнулся такую несколько странную возможность, как отключение компиляции через опцию -Dmaven.main.skip.
Почему, зачем? На первый взгляд - бессмысленно.
Но недавно на практике понял зачем она нужна.

Предположим в вашем pom есть некий плагин с достаточно сложными нестандартными настройками. Есть процесс, в котором нужно выполнить его goal. Если мы просто запустим goal командой mvn plugin:version:goal, то настройки из pom будут проигнорированы. Предположим, задать их из командной строки невозможно, т.к. плагин это не поддерживает. А даже если возможно - это будет дублирование, нарушение DRY, т.е. тоже плохой вариант)
Хуже всего то, что наш плагин находится в build lifecycle https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference после фазы compile. Пусть проект уже собран, но как известно инкрементальная компиляция в Maven существует, но с ней есть нюансы: https://stackoverflow.com/questions/16963012/maven-compiler-recompile-all-files-instead-modified/19653164#19653164
К слову - именно по скорости компиляции Maven сильно проигрывает Gradle.

Так вот - именно тут опция -Dmaven.main.skip и выходит на сцену, сильно уменьшая время выполнения.

P.S. Да, по моему опыту - плагин для компиляции Kotlin, увы, ее не поддерживает.

#maven #java #kotlin
Всем привет!

Уже был пост о том, как не надо использовать контекстные функции в Kotlin https://t.me/javaKotlinDevOps/189
Сразу может возникнуть вопрос - а когда их стоит использовать?

По ссылке уже был пример - для установки полей объекта, которые нельзя установить через конструктор. То ли потому, что подходящего конструктора\builder нет, то ли потому, что в момент создания объекта их значения еще не известны. Вот он:
val man = Person("Vasya").apply {
age = 20
city = "Moscow"
}

К слову - эту задачу можно было решить лет 20 назад в Delphi с помощью оператора with, мне его очень не хватало в Java))) И интересно, что в Kotlin with немного отличается по смыслу: https://kotlinlang.org/docs/scope-functions.html#function-selection

Еще кейс - выполнение какого-то нефункционального требования или вспомогательного кода, типа отбрасывания логов, метрик:

return claim.also {
log.info("Возвращаем $claim")
}

Ключевой момент здесь, о котором я буду постоянно говорить - код логично выстроен и легко читается: возвращаем объект заявки, а заодно его логируем.

И еще кейс - функция однострочник, без явного тела. Ключевой момент - это реально должна быть функция из одной или нескольких строк. Если сделать портянку из цепочки вызовов контекстных функций - читаемость только ухудшится, лучше бы были скобки.
Пример как надо:

fun main() = doSomething().also { doAnotherThing(it) }

Если подкинете еще хорошие примеры в комментариях - буду благодарен.

#kotlin #readability
Всем привет!

Я уже поднимал тему boolean параметров как антипаттерна https://t.me/javaKotlinDevOps/229. Давайте расширим ее до вопроса - когда стоит использовать if?
Является ли if антипаттерном?
По мнению некоторых товарищей - да, является: https://www.antiifprogramming.com/about-the-anti-if.php
Как по мне - не всегда, зависит от ситуации.
Чем плох if? // да, switch - это по сути тот же if.

1) может нарушать принцип Single Responsibility. Почему - думаю объяснять не нужно.
2) может ухудшать читаемость кода, я которую я всегда "топлю") Т.е. нарушает принцип KISS. Усугубляет ситуацию тот факт, что код как правило не остается неизменным. И обычный if else со временем может превратится в многоуровневого нечитаемого монстра.
3) может нарушать принцип Don't Repeat Yourself. Тут два очевидных варианта - либо во всех ветках if выражения есть дублирующийся код, либо чтобы обработать возврат некого метода всегда нужен if.
4) если в коде слишком много if (x != null) - это признак того, что вы неправильно работаете с nullability. Тут могу посоветовать Kotlin, т.к. он может сообщать о null значениях на этапе компиляции. Optional и его альтернативы в Java избавляют от NPE, но не избавляет от проверок на null. Я видел советы - просто не пишите код, который возвращает null - тогда проверки будут не нужны. Но это надежда на человеческий фактор, и компилятор (я про Kotlin) работает лучше)))

Да, я специально пишу везде слово "может". Бывают if-ы, которые не нарушают ни один из принципов.

Когда стоит волноваться?

1) подключаем SonarQube или Checkstyle и не игнорируем ошибки, связанные с цикломатической сложностью методов, см. https://t.me/javaKotlinDevOps/197
2) код просто сложно становится читать. Особенно хорошо эта проверка проходит на новых разработчиках)

Идеально конечно не писать код, приводящий к лишним if. Но я уже писал про человеческий фактор выше)

Что можно сделать? // будет некоторый повтор написанного тут https://t.me/javaKotlinDevOps/229

1) выделяем сложный код условия в отдельный метод.
2) вместо двух или более веток оператора if делаем несколько методов. Помогает в случае, если условно метод А всегда вызывает метод С с значением true, а метод Б - с значением false. Иначе будет как на знаменитой картинке - проблема не на моей стороне)))
3) используем not null объекты и переходим Kotlin
4) перепроектируем код, чтобы проверки выполнялись в одном месте, а не дублировались по коду. Для этого их придется перенести из вызывающего кода в вызываемый. И придумать правильное значение по умолчанию.
5) при необходимости вводим иерархию классов, чтобы каждый класс отвечал за одну ветку switch
6) используем паттерн Стратегия - по сути частный случай введения иерархии классов
7) используем паттерн Состояние (State), который кроме хранения состояния выполняет обработку, связанную с различными состояниями, тем самым убирая if из вызывающего кода

#antipatterns #if_antipattern #java #kotlin #solid #patterns #dev_compromises
Всем привет!

Хорошая статья про String Templates в Java 21 https://blog.jetbrains.com/idea/2023/11/string-templates-in-java-why-should-you-care/
Самое интересное в шаблонах Java вот что.
Лично у меня часто складывается впечатление, что Java копирует языковые фишки, они же синтаксический сахар, у Kotlin, Scala, C# и других языков. Копия иногда такая же по функционалу, иногда хуже. Повторюсь - именно это впечатление, т.к. строго говоря появление фичи А после фичи Б не значит, что она скопирована.

Но в данном случае строковые шаблоны получились хоть и не такими синтаксически простыми, но более крутыми по функционалу.
Выглядят они так:

STR."some string \{varWithMeaningfulName}"

Да, странно что \ вместо $, но самое важное здесь то, что шаблон является методом у некого класса. Это класс StringTemplate.Processor<String, RuntimeException>. В Java core есть ряд его стандартных реализаций, но самое главное - можно сделать свою. В статье выше есть парочка интересных примеров.

Как по мне - крутая фича! Жалко, что дотянули до 21-й Java, и то пока preview)))

#java #kotlin #strings #java_new_version
Всем привет!

Недавно вышла новая версия IDEA - 2024.1. В новой версии появилось много "вкусного", вот что я бы отметил:

1) если раньше аналог Copylot (Gigacode) был доступен только по отдельной подписке, и при этом сильно уступал Copylot, то сейчас в Ultimate появился "бесплатный" AutoCompletion. Проверил: в моем случае, предлагает компилируемый код, но требующий правок) Вообще вижу тенденцию, что часть изначально платных LLM инструментов становятся бесплатными - ChatGPT4, IDEA AI. В случае Microsoft - они просто делятся машинным временем для популяризации инструмента, в случае IDEA - работает локальная модель, которой как контекст подается код открытого проекта.

2) IntelliJ в курсе проблемы долгой инициализации проекта... ремарка - еще бы они не были в курсе, такое сложно не заметить) ... и сделала доступной ряд фич IDE во время инициализации. Плюс сейчас происходит быстрый парсинг maven pom, и на основе полученной информации становится доступной навигация по проекту пока идет индексация.

3) ряд улучшений по работе с логами:
- AutoCompletion кода для инициализации логгера, если в классе ее еще нет
- как альтернатива возможна автогенерация этого кода
- парсинг логов на предмет наличия там классов проекта.
Что я бы отдельно отметил на примере этой и предыдущей фичи - комплексный подход. Берем все (заведенные тикеты), что можем улучшить по теме - и улучшаем)

4) новая консоль. Главные фичи - снова AutoCompletion и разбиение единого полотна текста на команды, что облегчает их копирование. В будущем обещают улучшение AutoCompletion и даже подсказки по ошибкам. Видимо с использованием LLM. Жаль, пока работает только на bash и PowerShell, но думаю тоже доделают.

5) sticky режим при прокрутке больших файлов. Т.е. всегда видны декларация класса и текущего метода каким бы большим не был метод. Число видимых строк настраивается. Напомнило мне "хлебные крошки" на сайтах. Большие классы и методы конечно же зло, но это зло существует)

6) куча улучшение по работе с Pull request - можно ревьювить их в IDEA, в т.ч. смотреть diff, писать комментарии, смотреть статус prCheck и даже ставить лайки) К сожалению только GitHub и GitLab.

7) для самого частого рефакторинга - переименование - сделали inline режим: не надо вызывать команду по шорткату или меню - просто переименовываешь сущность, а IDEA сама предложит переименовать ее по всему проекту

8) появилась поддержка OpenRewrite - фреймворка для написания рефакторингов, если встроенные не устраивают. https://t.me/javaKotlinDevOps/116 Правильный подход, я считаю

9) и наконец на первый взгляд незаметная фича - альфа тестирование нового компилятора Kotlin K2. Казалось бы - ну и ладно, заменили компилятор, при чем тут IDE. А штука в том, что ребята специально переписали свой компилятор, чтобы его удобнее было использовать для фич IDEA - сделать то, что раньше не получалось и ускорить работу IDE. Т.е во-первых, интересен тот факт, что для подсветки синтаксиса, AutoCompletion и т.д используется компилятор, а во-вторых - удобно быть и разработчиком IDE, и разработчиком языка) Более этого, это было одной из причин разработки этого языка - https://t.me/javaKotlinDevOps/38

Вот пожалуй и все, что запомнилось.

#idea #kotlin
Всем привет!

Давно не писал про Kotlin, а в названии канала он есть на почетном втором месте) Исправляюсь.

Основная фишка Kotlin - это упрощение написания и чтения кода за счет упрощения языка. В чем упрощение: все типы - объектные, функция всегда возвращает результат, нет неявных преобразований, нет проверяемых исключений, некоторые стандартные паттерны (синглтон, делегат) стали частью языка - не нужно изобретать велосипед. Возможность переопределения операций и полноценные функциональные типы - на самом деле тоже. Операция - краткий общеупотребительный вариант метода, функцию можно передавать как объект не создавая для этого объект.
Но как всегда есть нюансы.

Вот например inline методы и связанный с ним reified https://www.baeldung.com/kotlin/reified-functions
При беглом знакомстве возникают 2 вопроса:
1) разработчики Kotlin загрязняют язык, ведь компилятор, а скорее JVM, сами справятся с inline?
2) Kotlin хакнул type erasure для generic?

Ответ на оба вопроса - нет.
И есть отличная статья на эту тему https://habr.com/ru/articles/775120/ Автора знаю лично, рекомендую почитать эту и другие его статьи про Kotlin.

Для ленивых ))) ответы:
1) inline нужен только для методов с параметрами функционального типа, чтобы избежать обвертывания функции в объект. Java компилятор не умеет работать с функциональными типами, увы
2) reified не нарушает спецификации Java, компилятор Kotlin лишь сохраняет тип там, где он его знает, и это касается только inline методов

И про простоту Kotlin в целом и сложность inline. Как выглядит процесс со стороны:
1) у нас полноценные функциональные типы
2) в коде их будет много
3) Java не умеет с ними работать
4) сделаем inline, чтобы не снизить производительность при работе с такими типами
5) появляются баги из-за inline, приходится вводить ключевые слова noinline и crossinline. Подробнее об этом есть в статье выше.
6) кто-то просит: раз при inline мы знаем исходный тип generic - давайте дадим возможность работы с ним, появляется reified
7) возникают новые баги, их фиксят
...

Процесс вымышленный, возможно, в реальности было по-другому. Я хотел подчеркнуть вот что: одна фича тянет за собой другую, другая - несколько особых случаев. И все это усложняет язык, хотя цель была противоположная.

P.S. Ну и да, получается, во всем виновата Java)

#kotlin #java