在Scala中,为什么我可以在这里使用`Unit`作为返回类型?

时间:2015-02-17 14:58:27

标签: java scala types functional-programming type-inference

代码如下:

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)甚至使用ListAnyRef的子类型作为返回值,但REPL仍然没有抱怨这个...

有没有人对Unit可以容忍非子类型转换的原因有疑问?谢谢!

3 个答案:

答案 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不知何故是两者的不完全混合。