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