以下是一个例子:
$ scala
Welcome to Scala 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_112).
Type in expressions for evaluation. Or try :help.
scala> val a: Unit = 1
<console>:11: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
val a: Unit = 1
^
a: Unit = ()
在Scala文档中:
There is only one value of type Unit, ()
为什么Scala编译器会默默地将值强制转换为Unit?
一些上下文:我使用Future[Unit]
类型来描述一些不返回任何内容的过程。由于Future[Unit]
现在实际上是Unit
的子类型,我得到了一些有趣的错误(someFuture.map(a => Future(a))
默默地跳过调用操作而不是给出编译警告)。我应该使用什么作为一种不会返回任何有意义结果的操作?
答案 0 :(得分:5)
Unit
不是其他类型的超类型。相反,所谓的值丢弃称为:当表达式e
的预期类型为Unit
时,编译器将其替换为{e; ()}
。这样做是为了使某些行为更加熟悉。例如。
val sb = new StringBuilder
val strings: List[String] = ...
for (str <- strings) { sb.append(str) }
与其他语言中的for
循环类比,我们希望它能够编译。但是,如果没有价值丢弃它就不会:strings.foreach(str => sb.append(str))
相当于str => sb.append(str)
的类型String => StringBuilder
(因为append
上的所有StringBuilder
方法返回构建器本身)foreach
上的List[String]
需要String => Unit
。
您可以添加-Ywarn-value-discard
编译器选项,以便在发生时向您发出警告(并明确写入for (sb <- sbOpt) { sb.append("a"); () }
)。
或者您实际上可以使用a trick定义自己的Unit
(可能更改名称以避免混淆任何人阅读您的代码):
object Unit
type Unit = Unit.type
implicit def unit2scalaunit(a: Unit): scala.Unit = ()
implicit def scalaunit2unit(a: scala.Unit): Unit = Unit
这应避免遇到您所描述的Future
问题。
答案 1 :(得分:1)
Unit
不是一切的超类型! Scala实际上有各种各样的转换自动发生,这就是其中之一。从Scala语言规范的section 6.26.1 Value Conversions开始,其中一次转化是
价值丢弃
如果
e
具有某种值类型且预期类型为Unit
,则会转换e
通过将其嵌入术语{ e; () }
中来预期类型。
因此,当您编写类似val a: Unit = 1
的内容时,它会被处理为val a: Unit = { 1; () }
,这是完全不同的。这里的警告实际上非常有用 - 它警告你你可能做错了:你试图放入语句位置的表达式是纯的(没有副作用),所以执行它没有任何效果(除非可能导致关于最终产出的分歧程序。