我遇到了这种非常特殊的行为,让我难以忍受了一段时间。我在下面的一个简单片段中重新创建了它,代码从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
是否有其他人经历过这种情况或对此为何发生过任何见解?
答案 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}}。