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
函数可能会发生。 也就是说,开发人员只需要在单独的一行中写入函数调用即可。