Котлин: новые заклинания версии 2.1.0✨

Котлин: новые заклинания версии 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 можно в тикете.

Report Page