使用隐式val设置为null,Scala编译器的行为不符合预期

时间:2015-08-20 17:41:17

标签: scala

我遇到了这种非常特殊的行为,让我难以忍受了一段时间。我在下面的一个简单片段中重新创建了它,代码从Scala包装器中取出。

scala> def a = {
 | implicit val u = null
 | val x: Int = List(1,2,3).map(_.toString)
 | }
a: Unit

在上面的代码中,即使我知道val x的类型是List [String],也没有抛出错误。我可以将x的类型更改为Int,Long等,它将继续编译。

但是,当我为隐式值添加显式状态时,如下例所示,编译器按预期运行并抛出错误。

scala> def a = {
 | implicit val u: Any = null
 | val x: Int = List(1,2,3).map(_.toString)
 | }
<console>:10: error: type mismatch;
 found   : List[String]
 required: Int

是否有其他人经历过这种情况或对此为何发生过任何见解?

2 个答案:

答案 0 :(得分:8)

由于在scala中设计集合的方式,null被选为map的隐含参数,其具有以下扩展签名:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That

在此特定示例中,隐式参数必须是CanBuildFrom[List[Int], String, Int],不幸的是null是底部类型,因此它满足此类要求。

查看此质量检查了解详情:official documentation

你可以在没有隐含的情况下重现它:

@ def a = {
    val x: Int = List(1,2,3).map(_.toString)(null)
  }
defined function a

然后,当你实际运行该函数时,它会尝试应用构建器,但它是null,所以... BOOM!

@ a
java.lang.NullPointerException
        scala.collection.TraversableLike$class.builder$1(TraversableLike.scala:240)
        scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
        scala.collection.immutable.List.map(List.scala:285)
        cmd3$.a(Main.scala:52)
        cmd4$$anonfun$1.apply$mcV$sp(Main.scala:52)
        cmd4$.<init>(Main.scala:53)
        cmd4$.<clinit>(Main.scala:-1)

您可以在堆栈跟踪中看到,TraversableLike上的地图实施尝试访问builder时,一切都会中断,这恰好是null

答案 1 :(得分:4)

当你查看map的签名时,你会发现有一个隐含的参数:

 def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That

当编译器在map的调用范围内看到隐式值时,它似乎认为它应该是CanBuildFrom[List[Int], String, Int]。所以它扩展到:

def a = {
  implicit val u: CanBuildFrom[List[Int], String, Int] = null
  val x: Int = List(1, 2, 3).map(_.toString)(u)
}

当您尝试使用它时,您可以期待NullPointerException

现在,当您为u提供显式类型时,编译器将无法再找到或推断出会给出结果类型CanBuildFrom的{​​{1}}。