Каковы правила вывода точки с запятой в Котлине?

Kotlin предоставляет "вывод точки с запятой": синтаксически подстановки (например, операторы, объявления и т. д.) разделяются псевдо-токеном SEMI, что означает"точка с запятой или новая строка". В большинстве случаев в коде Котлина нет необходимости в точке с запятой.

Это грамматика страница говорит. Это, по-видимому, означает, что в некоторых случаях необходимо указать точки с запятой, но это не указывает их, и дерево грамматики ниже не совсем сделайте это очевидным. Также у меня есть подозрения, что есть некоторые случаи, когда эта функция может работать неправильно и вызывать проблемы.

Итак, вопрос в том, когда нужно вставить точку с запятой и какие угловые случаи нужно знать, чтобы избежать написания ошибочного кода?

3 ответов


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

правила: не беспокойтесь об этом и не используйте точки с запятой вообще (кроме двух случаев ниже). Компилятор скажет вам, когда вы ошибетесь, гарантировано. Даже если вы случайно добавите дополнительную точку с запятой, подсветка синтаксиса покажет вам, что это ненужно с предупреждением о "избыточной точке с запятой".

два общих случаях точки с запятой:

класс перечисления, который имеет список перечислений, а также свойства или функции в перечислении, требует ; после списка перечислений, например:

enum class Things {
    ONE, TWO;

    fun isOne(): Boolean = this == ONE
}

и в этом случае компилятор скажет вам напрямую, если вы не сделаете это правильно:

ошибка: (y, x) Котлин: ожидание '; ' после последней записи перечисления или '} 'чтобы закрыть enum class body

в противном случае единственный другой общий случай, когда вы делаете два утверждения на одной строке, возможно, для краткости:

myThingMap.forEach { val (key, value) = it; println("mapped $key to $value") } 

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

в прошлом были другие случаи, такие как блок инициализации класса, который был более "анонимным" { ... } до Котлина 1.0 и позже стало init { ... } который больше не нуждался в точке с запятой, потому что он намного яснее. Эти случаи больше не остаются в языке.

уверенность в этой функции:

также у меня есть подозрения, что в некоторых случаях эта функция может работать неправильно и вызывать проблемы.

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

просматривая все мои Котлин с открытым исходным кодом и наши внутренние довольно большие Котлинские проекты, я не нахожу никаких полуколонов, кроме случаев выше-и очень мало в итоге. Поддерживая понятие "не использовать точки с запятой в Котлине", как правило.

однако возможно, что вы можете намеренно придумать случай, когда компилятор не сообщает об ошибке, потому что вы создали код, который действителен и имеет другое значение с точкой с запятой и без нее. Это будет выглядеть следующим образом (измененная версия ответа @Ruckus):

fun whatever(msg: String, optionalFun: ()->Unit = {}): () -> Unit = ...

val doStuff: () -> Unit = when(x) {
    is String -> {
        { doStuff(x) }
    }
    else -> { 
        whatever("message") // absence or presence of semicolon changes behavior
        { doNothing() }
    }
}

в этом случае doStuff присваивается результат вызова whatever("message") { doNothing() } что является функцией типа ()->Unit; и если вы добавляете точку с запятой, ей назначается функция { doNothing() } что тоже типа ()->Unit. Таким образом, код действителен в обоих направлениях. но я не видел, чтобы что-то подобное происходило естественно так как все должно быть идеально. The характеристика предложил emit ключевое слово ^ оператор шляпа сделал бы этот случай невозможным, и он был рассмотрен, но отброшен до 1.0 из-за сильно высказанных мнений и времени ограничения.


Я дополнение к ответу Джейсона Минарда, я столкнулся с еще одним странным случаем края, где требуется точка с запятой. Если вы находитесь в блоке оператора, который возвращает функцию без использования оператора return, вам нужна точка с запятой. Например:

val doStuff: () -> Unit = when(x) {
    is String -> {
        { doStuff(x) }
    }
    else -> { 
        println("This is the alternate");  // Semicolon needed here
        { doNothing() }
    }
}

без точки с запятой Котлин думает, что { doNothing() } оператор является вторым аргументом для println() и компилятор сообщает об ошибке.


Котлин, кажется в основном выводите точки с запятой с нетерпением. Кажется, есть исключения (как показано в Примере перечисления Джейсона Минарда).

как правило, система типов будет ловить плохо выведенные точки с запятой, но вот некоторые случаи, когда компилятор терпит неудачу.

если аргументы вызова находятся в следующей строке (включая скобки), Котлин предположит, что аргументы являются просто новым выражением в скобках:

fun returnFun() : (x: Int) -> Unit {
  println("foo")
  return { x -> println(x) }
}

fun main(args: Array<String>) {
    println("Hello, world!")
    returnFun()
       (1 + 2)    // The returned function is not called.
}

A больше общим случаем может быть следующий, где у нас есть возврат с выражением в следующей строке. В большинстве случаев система типов будет жаловаться, что нет возвращаемого значения, но если возвращаемый тип Unit, тогда все ставки выключены:

fun voidFun() : Unit {
    println("void")
}

fun foo() : Unit {
    if (1 == 1) return 
    voidFun()  // Not called.
 }

fun bar() : Unit {
    if (1 == 1)
        return 
            voidFun()  // Not called.
 }

на bar функция потенциально может произойти, если return voidFun() не поместится на одной линии. Тем не менее, разработчики должны просто написать вызов функции в отдельной строке.