Kotlin中分号推理的规则是什么?

Kotlin提供了“分号推理”:语法上,子句(例如,语句,声明等)由伪标记SEMI分隔,后者代表“分号或换行符”。 在大多数情况下,Kotlin代码中不需要分号。

这是语法页面所说的。 这似乎意味着在某些情况下需要指定分号,但是它没有指定它们,而下面的语法树并没有明确地表示这一点。 此外,我怀疑有些情况下,此function可能无法正常工作,并导致问题。

所以问题是应该在什么时候插入一个分号,以及为了避免编写错误的代码,需要注意哪些角落情况?

在编译器模糊的情况下,只需要指定分号,而不使用分号就会导致明显的编译错误。

规则是:不要担心这个,根本不要使用分号(除了下面两种情况)。 编译器会告诉你什么时候出错,保证。 即使你不小心添加了一个额外的分号,语法突出显示也会显示“多余的分号”的警告。

两个常见的分号为:

一个枚举类有枚举的列表,还有枚举中的属性或函数需要一个; 在枚举列表之后,例如:

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

在这种情况下,编译器会直接告诉你,如果你不能正确地做到这一点:

错误:(y,x)Kotlin:期待’;’ 在最后一个枚举输入之后或者’}’关闭枚举类体

否则,唯一的另一个常见情况是,当你在同一行上做两个陈述时,也许为了简洁起见:

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

在这最后一个例子中没有分号会给你一个更加神秘的错误,在这个错误的地方,你正在做什么。 这是非常困难的,使一些代码,这将是有效的两个语句分隔分号,也是有效的,当分号被删除,他们成为一个。

在过去,还有其他一些类似于Kotlin 1.0之前的“匿名” { ... }类的初始化块,后来变成了init { ... } ,因为它更清晰了,不再需要分号了。 这些案件不再保留在语言中。

对这个function的信心:

此外,我怀疑有些情况下,此function可能无法正常工作,并导致问题。

该function运行良好,没有任何证据表明这个function存在问题,多年的Kotlin经验还没有发现任何已知的情况下,这个function回火。 如果有一个失踪的问题, 编译器会报错。

在搜索我的所有开源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被分配了调用的结果,它是type ()->Unit函数的whatever("message") { doNothing() } ; 如果你添加一个分号,它被分配了同样是type ()->Unit的函数{ doNothing() } 。 所以代码是有效的两种方式。 但是我还没有看到这样的事情发生,因为一切都必须完美地排列。 建议emit关键字或^帽子运算符的function会使这种情况变得不可能,而且由于强烈的反对意见和时间限制,它被认为在1.0之前就被抛弃了。

除了Jayson Minard的回答之外,我还遇到了另外一个需要分号的奇怪边缘情况。 如果你在一个不使用return语句的情况下返回一个函数的语句块,你需要一个分号。 例如:

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

没有分号,Kotlin认为{ doNothing() }语句是println()的第二个参数,编译器报告错误。

Kotlin似乎主要是推断分号。 似乎有例外(如Jayson Minard的enum例子所示)。

一般来说,types系统会得到错误的推断分号,但这里有一些编译器失败的情况。

如果调用的参数在下一行(包括括号)中,Kotlin会假设参数只是一个新的括号expression式语句:

 fun returnFun() : (x: Int) -> Unit { println("foo") return { x -> println(x) } } fun main(args: Array) { println("Hello, world!") returnFun() (1 + 2) // The returned function is not called. } 

更常见的情况可能是以下情况,我们在下一行中返回expression式。 大多数时候types系统会抱怨没有返回值,但是如果返回types是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. } 

如果return voidFun()不适合在一行上, bar函数可能会发生。 也就是说,开发人员只需要在单独的一行中写入函数调用即可。