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

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

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

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

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

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

Два общих случая для полуколонок:

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

 enum class Things { ONE, TWO; fun isOne(): Boolean = this == ONE } 

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

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

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

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

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

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

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

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

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

Поиск всех моих open-source Kotlin и наших внутренних довольно больших проектов 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 . Таким образом, код действителен в обоих направлениях. Но я не видел, чтобы что-то подобное происходило естественным образом, потому что все должно идеально сочетаться. Функция, предложенная для использования ключевого слова emit или оператора hat , сделала бы этот случай невозможным, и он был рассмотрен, но упал до 1.0 из-за сильно опровергнутых мнений и временных ограничений.

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

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

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

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

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

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

 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. } 

Более распространенным случаем может быть следующее: мы имеем возврат с выражением в следующей строке. В большинстве случаев система типов будет жаловаться на отсутствие возвращаемого значения, но если тип возврата – 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() не будет помещаться в одну строку. При этом разработчики просто должны написать вызов функции на отдельной строке.

Давайте будем гением компьютера.