Каковы правила вывода точки с запятой в Котлине?
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() не поместится на одной линии. Тем не менее, разработчики должны просто написать вызов функции в отдельной строке.