这个例子是一次错误的尝试时发现的,通常我们不太会用Unit类型声明参数,更多情况它是出现在函数类型声明里的出参部分(如 String=>Unit)。这个例子仅用作案例分析,先考虑下面的方法执行结果会如何?
scala> def foo(p:Unit) = {println("hi")}
foo: (p: Unit)Unit
scala> foo(2)
直觉上,感觉编译会报错,类型不匹配么,声明Unit类型,传入的确实Int类型。
实际上尝试一下会发现,它编译和运行都很正常。甚至无所谓你传入几个参数,或什么类型:
scala> foo(2,3,"OK")
hi
都能正常运行,看起来太诡异了。这背后隐含了编译器对Unit类型变量在赋值时的处理逻辑。
Unit类型是值类型,全局只存在唯一的值:()
scala> val a:Unit = ()
a: Unit = ()
若尝试把其它类型的值赋给它,也ok:
scala> val a:Unit = 2
a: Unit = () // 暂忽略编译器的警告,跟我们要关注的无关
诊断一下:
$ scala -nocompdaemon -Xprint:typer -e 'val a:Unit = 2'
…
private[this] val a: Unit = {
2;
() // 编译器自动增加了一个Unit的值:()
};
…
原因是编译对待等号右边的表达式,看待成为“行为”,会把这段“行为”统一放入花括号里。
并判断最后一句表达式的值是不是Unit类型的,如果不是则自动增加一个值:()
所以等号右边的 2
最终被转化为 { 2; () }
所以 foo(2,3,"OK")
也是被翻译为了
foo( {(2, 3, "OK"); ()} )
参数封装在一个tuple里,在最后返回一个 ()
再探究一步,Unit类型通常是用于声明方法返回值的,比如:
def foo:Unit = 2
它被翻译为:
def foo: Unit = {
2;
() // 编译器判断结果返回不是Unit类型的话,自动在最后返回()
}
当Unit用在值的类型时,编译器保持与方法一致的处理逻辑。
小结:通常Unit只用来声明函数或方法的返回值,其他场景基本是没有意义的。
Pingback引用通告: scala雾中风景(8): 高阶函数与Unit的谜题 | 在路上
哈哈,google scala Unit 排名第一的文章,好久没有来博主的blog了,来一次收获一次,多谢!
google这么给面子呢,欢迎常来
Pingback引用通告: scala雾中风景(17): toSet()的谜题 | 在路上
Pingback引用通告: scala雾中风景(21): auto-tupling与auto-detupling | 在路上
scala> reify(val a = 2)
:1: error: illegal start of simple expression
reify(val a = 2)
竟然不对,看来val a:Unit = 2不是表达式(expression)而是个赋值语句(statement)导致的?