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

Чтобы проиллюстрировать предыдущий пост - предлагаю сравнить по критерию 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