company_banner
25 сентября 2012 в 07:28

Вышел Kotlin M3

Наш язык программирования неуклонно развивается: мы выпустили Kotlin M3 — большой milestone, в который вошло много интересного: от обновления домашней страницы до поддержки режима скриптов. А еще наша команда начала активный «догфудинг»: в ближайшее время все больше кода в проекте Kotlin будет писаться на Kotlin.

В этом посте я кратко опишу две наиболее интересные вещи, которые были сделаны в M3: мульти-декларации и «расщепление» интерфейсов коллекций.

Мульти-декларации



Как это выглядит. Теперь в Kotlin можно писать, например, такой код:

val map = hashMap("one" to 1, "two" to 2, "three" to 3)
for ((k, v) in map) {
    println("$k -> $v")
}


Обратите внимание на то, что в for объявлена не одна переменная, а две, в круглых скобках. Это один из примеров множественных деклараций, или «мульти-деклараций» (multi-declarations). Более простой пример:

val (a, b) = pair


В результате такого объявления появляются две переменные, которые можно использовать абсолютно независимо друг от друга.

Как это работает. Декларация из последнего примера транслируется в такой код:
val a = pair.component1()
val b = pair.component2()


Как и многое другое в Kotlin, мульти-декларации опираются на конвенцию: функции componentN() вызываются по имени, то есть могут быть объявлены как в классе pair, так и вне его — в качестве extensions. Это дает возможность обходить map так, как было показано в начале: для Map.Entry объявлены две extension-функции:
fun <K, V> Map.Entry<K, V>.component1() = getKey()
fun <K, V> Map.Entry<K, V>.component2() = getValue()


В общем случае для каждого класса нужно реализовывать такие функции по-своему, но есть один очень распространенный случай: когда класс просто хранит данные и больше ничего не делает. Это называется «класс данных» (data class):

data class User(val name: String, val password: String) {}


Аннотация «data» сообщает компилятору, что нужно сгенерировать в этом классе несколько стандартных методов: equals()/hashCode(), toString() и component1(), component2(). Все эти функци используют свойства класса, объявленные в первичном конструкторе, например, component1() возвращает name, а component2() — password:

val (name, pass) = getUser()


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

Еще про мульти-декларации в этом посте по-английски.

Типы коллекций



Коллекции используются везде и всегда, поэтому от дизайна соответствующей части библиотеки во многом зависит удобство языка программирования. Именно поэтому многие «альтернативные» языки на платформе Java используют собственные коллекции, несовместимые со стандартными, что создает различные трудности при использовании Java-библиотек (которых большинство и благодаря которым Java — такая замечательная платформа).

Kotlin делает упор на совместимость с существующим кодом, поэтому мы используем стандартные классы коллекций, но делаем их лучше:
image

Обратите внимание, что интерфейсы коллекций делятся на read-only и mutable. Старые добрые java.util.ArrayList и HashSet по-прежнему доступны — это изменяемые коллекции. Но если где-то написано просто List<String>, изменять его нельзя.

Это помогает не только навести порядок, но и упростить себе жизнь: теперь можно присваивать List<String> туда, где ожидался List<Any>, потому что неизменяемые коллекции ковариантны.

Заключение



В M3 вошло еще много чего интересного. Подробнее об этом можно почитать здесь (по-английски).

Инструкция по установке — здесь (нужна IntelliJ IDEA 12).

Как всегда, мы будем благодарны за Ваши замечания/соображения/пожелания. Приятного Kotlin'а :)
Автор: @abreslav
JetBrains
рейтинг 273,81

Комментарии (27)

  • +1
    data class User(val name: String, val password: String) {}

    val (name, pass) = getUser()

    А что будет со второй строчкой, если я позже в User добавлю после имени, но до пароля еще, скажем, email? Код перестанет компилироваться? Или я внезапно получу в pass emailы?
    • +1
      И еще интересно если для защиты от подобных проблем будет требоваться чтобы использованы были все componentN, то будет ли способ явно «проигнорировать» часть из них, например так:

      val (name, _, pass) = getUser()

      вместо

      val (name, email, pass) = getUser()
    • 0
      В текущей версии в pass будет записан email. Думаю, что Вы привели хороший довод в пользу того, чтобы требовать, чтобы все компоненты были присвоены (но какие-то можно было пропутстить).
      • 0
        Прадва, по некотором размышлении, не очень понятно, как это сделать, имея в виду, что data-классы никакие не особенные, у них просто есть соответствующие методы…
        • +3
          Я бы предложил выкинуть convention про componentN вообще из языка как привносящую слишком много хрупкости в программы. А в замен сделал бы что-то подобное:

          val (name, pass) = getUser.(name, password)
          


          предполагая, что это скомпилируется в:

          val tmp = getUser();
          val name = tmp.name();
          val pass = tmp.password();
          


          Да и вообще, все неявные фишки языка (когда компилятор догадывается до чего-то сам), которые выглядят чрезвычайно привлекательно для быстрого прототипирования (тупо меньше кода писать), могут при этом очень незаметно для простого разработчика привносить много хрупкости и создавать много боли и страданий при дальнейшей поддержке.
          • +1
            ой, я скобочки у getUser забыл. Имел в виду вот так:

            val (name, pass) = getUseк().(name, password)
            
          • +1
            Поддерживаю. Data class определён с _именами_ полей, вот пусть они по именам и достаются. Привязываться к _порядку_ объявления, да ещё и неявно — это самому себе стелить грабли.

            Вообще, при всём уважении к команде Котлина (и симпатии к языку), componentN — это уродство. Надеюсь, можно будет придумать какую-нибудь альтернативу. Например, var (a, b) = c автоматически заполнять, если c — это массив (список, итератор и т. п.), а var (foo: a, bar: b) = c или (a, b) = c.(foo, bar), если c — это объект (или хэш?).
  • 0
    Не планируется ли «скрестить» мульти-декларации с конструкцией when так же, как это сделали с конструкцией for?
    • 0
      Не очень понятно, как это сделать, чтобы не возникало традиционной в таких случаях путаницы между объявлением переменной и ее использованием.
      • 0
        Например так:

        when(obj) {
          (name, email, _) is User -> println("User name: $name, email: $email.")
        }
        


        Кстати, какой вариант синтексиса Вы использовали в статье дя выделения кода? Kotlin в списке нет.
        • 0
          Надо только не "(name, email, _) is User", a «User (name, email, _)» — тип должен быть частью паттерна.
          • 0
            Так нету там паттернов. Точнее якобы есть, но там только проверка типа.

            Более того ранее я слышал заявления, что реализации паттернов не будет.

            А перед «is» я поставил в данном случае чтобы
            1) «чтобы не возникало традиционной в таких случаях путаницы между объявлением переменной и ее использованием»
            2) Чтобы не так явно намекать на scala.
  • +1
    Есть хотя бы весьма примерная дата выхода версии 1.0 (скажем 2013-2014 или как-то так)? Я пробовал использовать язык в своих «игрушечных» проектах и в принципе остался доволен.

    В нем больше ясности, больше понимания того, что «под капотом» происходит по сравнению с той же Scala. Scala прекрасный язык, просто переключиться на него после Н лет работы с Java весьма сложно.
    • 0
      Мы планируем начать широко использовать Kotlin внутри JetBrains в 2013 году. Эта стадия будет называться Beta. Доступ к ней, естественно, получат все.

      Дату релиза 1.0 назвать невозможно: она зависит от того, что выяснится в процессе апробации языка в бета-стадии. Если придется менять что-то серьезное, это может сильно задержать релиз.
  • +3
    Я лично нетерпением жду новостей от JetBrains по вопросу Nemerle :) Было бы крайне приятно видть аналогичный пиар по этому прекрасному языку
  • 0
    А почему «мульти-декларации», а не «паттерн-матчинг»?
    • 0
      Потому, что от реализации сопоставления с образцом отказались.

      Сделали свой when с проверкой либо на равенство, либо на тип.
    • 0
      Потому что это не паттерн-мэтчинг. Никакого сопоставления с образцом не происходит: только декларация переменных.

      Настоящий паттерн-мэтчинг мы решили пока отложить.
      • 0
        Настоящий паттерн-мэтчинг мы решили пока отложить.
        А почему??
        • 0
          Потому что это очень сложная штука, 80% пользы от которой без проблем покрывается тем, что есть сейчас: github.com/abreslav/introduction-to-kotlin/blob/master/kotlin-examples/src/_06_smart_cast/Eval.kt#L52
          • 0
            дык сейчас у вас вроде обычный if чуть засахаренный, насколько я вижу.
            А PM в использовании ничуть не сложен.
            Expr =
            | Num of Integer
            | Sum of Expr * Expr
            | Mult of Expr * Expr

            let evalWhen = function
            | Num(x) -> x
            | Sum(a, b) -> evalWhen(a) + evalWhen(b)
            | Mult(Num(0), _) | Mult( _, Num(0)) | -> 0 // типа оптимизация вычислений. в вашем текущем синтаксисе это выльется во всякую совсем мутную писанину.
            | Mult(a, b) => evalWhen(a) * evalWhen(b )

            Причем мне кажется именно для задач анализа AST такое архиполезно. да и не только AST вообще в любой задаче где нуно хоть сколько нить сложные данные анализировать.
            • 0
              Это архиполезно только для задач анализа AST. У нас язык для людей, а не для анализа AST :)
  • 0
    А как с производительностью компилятора и плагина? Есть изменения в лучшую сторону?
    • 0
      Есть: в этом майлстоуне ускорили code completion и еще некоторые сервисы в IDE. Кое-что и в компиляторе, но там еще много-много работы.
  • 0
    Новое ключевое слово 'data' — не феншуйно. Подозреваю, вы пошли на это с трудом.
    • 0
      Совершенно без труда, поскольку это не ключевое слово, а аннотация.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка