Котлин: новые заклинания версии 2.1.0✨
@anyonepawПрошло полгода с выхода нашумевшего релиза Kotlin 2.0. Сейчас мы на версии 2.1.0, и по официальной документации минорный релиз - это языковой релиз, так что предлагаю залететь в идею на выходных и попробовать, что разработчики представили нам в этот раз:
Внимание: магия работает только с последним плагином kotlin (аka последняя версия intelliJ), в режиме K2 компилятора (как включать здесь), да, и версию плагина у сборки проставить в 2.1.0, проставить флаги компилятора, соответствующие фичам и сбилдить пет-проект. Зато приготовления стоят того!
<!-- Например, в maven флаги будут проставлены так: -->
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId
<version>${kotlin.version}</version>
<configuration>
<args>
<arg>-Xmulti-dollar-interpolation</arg>
<arg>-Xwhen-guards</arg>
<arg>-Xnon-local-break-continue</arg>
</args>
</configuration>
</plugin>
Иногда флаг может не подхватиться идеей сразу, поэтому нужно убрать чек на Enable K2 mode и перезапустить IDE, и снова поставить.
Guard conditions in when with a subject
Больше не нужно городить условия после веток у when (subject тут означает when которому передается объект, с обычным when фокус не сработает), ведь if’ы появляются прямо в ветках проверки:
interface Animal {
class Cat(val mouseHunter: Boolean) : Animal {
fun feedCat() {
println("Cat is fed")
}
}
data object Dog : Animal {
fun feedDog() {
println("Dog is fed")
}
}
}
fun feedAnimal(animal: Animal) {
when (animal) {
is Cat if !animal.mouseHunter -> animal.feedCat()
is Dog -> animal.feedDog()
}
}
Можно назвать это “охранным условием”, но я бы предложила “условие-страж”. Код с котом и условием-стражем сработает, если ветка when и сам страж удовлетворяют условию. Если поставить коту true, нам ничего не вернется, так как страж не отработает и ветка будет пропущена. Страж может быть только один, но можно продублировать ветку when:
is Status.Error if status.problem == Problem.CONNECTION -> "problems with connection" is Status.Error if status.problem == Problem.AUTHENTICATION -> "could not be authenticated"
И еще можно его и в ветку с else добавить:
else if true -> "some" else -> "unknown"
KEEP у условий-стражей подробный, можно посмотреть разные примеры использования.
Кстати, еще в Java 21 перед этим вышел свой страж: guarded pattern case labels
static void testNew(Object obj) {
switch (obj) {
case String s when s.length() == 1 -> ...
case String s -> ...
...
}
}
У джавы эта фича называется “pattern matching” и поддерживается в switch JEP-441 и records JEP-440. У котлина же страж работает через smart-casts.
Non-local break and continue in inline lambdas
Эта фича долгожданная, так что без ожидания отзывов, она пойдет в следующий языковой релиз 2.2.0. У Kotlin есть три jump-expressions: return, continue, break, и, если в inline-функциях уже была поддержка non-local return, вот такого:
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true // returns from hasZeros
}
return false
}
То для циклов не было логичной поддержки break и continue, поэтому, приходилось обходиться существующим return:
fun foo() {
run {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // non-local return to run
print(it)
}
}
print("done with nested loop")
}
Теперь в подобном цикле в лямбде inline-функции поддерживаются break и continue:
fun foo() {
run {
for (it in listOf(1, 2, 3, 4, 5)) {
if (it == 3) break else continue // non-local break to run
print(it)
}
}
print("done with nested loop")
}
Такие выходы из циклов пригодятся во вложенных циклах, например в таких. И в целом, KEEP с предысторией достаточно интересный.
Multi-dollar string interpolation
Если в однострочном строковом шаблоне символ доллара для литерала экранируется просто: обратной чертой “\$someValue”, то в многострочном тексте (так называемой фиче “multiline string/interpolation” в зависимости от того, есть в строке литералы или нет) такое экранирование уже не сработает, приходится оборачивать сам знак в литерал “””${“$”}”””. Теперь такую запись упростили до $$.
val jsonSchema = $$"""
{
"$id": "https://example.com/product.schema.json",
"title": "$${simpleName ?: qualifiedName ?: "unknown"}",
}
"""
А почитать про проблематику и найти KEEP можно в тикете.