代码如下:
scala> def f(x:Int => Unit):Unit = 1
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
def f(x:Int => Unit):Unit = 1
^
f: (x: Int => Unit)Unit
scala> f(_=>2);
<console>:9: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
f(_=>2);
^
scala> f(_=>List(1,2));
以上所有三个表达式都在REPL中工作(带有一些警告),但它们看起来有点令人困惑......
在第一个表达式中,f
的返回类型为Unit
,这是AnyVal
的子类型,但不是Int
的超类型,因此,我可以'理解为什么1
可以用作返回值。
在第二个表达式中,_=>2
也使用2
而不是Unit
作为返回值,这与定义冲突。
在第三个表达式中,_=> List(1,2)
甚至使用List
,AnyRef
的子类型作为返回值,但REPL仍然没有抱怨这个...
有没有人对Unit
可以容忍非子类型转换的原因有疑问?谢谢!
答案 0 :(得分:12)
在这种情况下,Scala会自动插入()
(单例Unit
值)以进行类型检查。所以你所拥有的相当于:
def f(x:Int => Unit):Unit = { 1; () }
这在Scala中被称为“价值丢弃”。来自spec:
价值放弃
如果
e
具有某种值类型且预期类型为Unit
,则e
会通过将其嵌入到期限中而转换为预期类型{ e; () }
与许多编程语言一样,这意味着只是“抛弃”表达式的返回值。这允许您创建仅使用表达式的副作用的类型Unit
的方法。
答案 1 :(得分:2)
检查SLS中的implicit conversions部分
<强> ValueDiscarding。强> 如果e具有某种值类型且预期类型为Unit,则通过将e嵌入到术语{e;中来将e转换为期望类型。 ()}。
答案 2 :(得分:0)
除了Ben Reich的回答:
从概念上讲,在Scala中,类型Unit
是所有其他类型的超类型。因此,每个值(包括Int
)都可以分配给Unit
。实际上,如果用作方法的返回类型,编译器将丢弃结果值,为方法提供JVM / Java void
类型。
Nothing
正好相反:从概念上讲,它是所有其他类型的子类型。由于不存在Nothing
的实例,因此任何其他类型的实例都不能与Nothing
分配兼容。
Java void
不知何故是两者的不完全混合。