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

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

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

19
Malcolm 4 Сен 2016 в 18:19

3 ответа

Лучший ответ

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

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

Два распространенных случая использования точки с запятой:

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

enum class Things {
    ONE, TWO;

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

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

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

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

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

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

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

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

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

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

Обыскивая весь мой Kotlin с открытым исходным кодом и наши внутренние довольно большие проекты 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 ключевого слова или ^ оператора шляпы будет иметь сделали этот случай невозможным, и он был рассмотрен, но отклонен до версии 1.0 из-за резко противоположных мнений и ограничений по времени.

25
Jayson Minard 6 Сен 2016 в 14:41

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

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

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

9
Jayson Minard 6 Сен 2016 в 14:29

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

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

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

7
Florian Loitsch 19 Янв 2017 в 21:26