Void 和 intersection 类型的奇怪行为

Strange behavior of Void and intersection types

这是预期的行为吗?

scala> val l: List[Void] = "I'm void".asInstanceOf[String with Void] :: Nil
l: List[Void] = List(I'm void)

l 识别为 List[Void] 并将 String 显示为第一个元素是否有效?

从某种意义上说,这是完全正确的,并不特定于 Void"I'm void".asInstanceOf[String with Void] 本身 不会 抛出 ClassCastException 直到您实际尝试以需要将其视为无效类型的方式使用它。

val l: List[Void] = "I'm void".asInstanceOf[String with Void] :: Nil

这是有效的,因为我们在欺骗编译器说 "I'm void"String with Void 的实例,而 String with Void 是 [=13= 的子类型],这意味着我们可以 暂时 有一个 List[Void],因为 List[String with Void] <: List[Void]但是,如果我们尝试访问List[Void]的头部,我们期望Void,而原来的String不是。

scala> l.head
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Void

一开始它似乎可以工作,因为所有东西都有一个 toString 方法,所以它不需要被视为 Any 以外的任何东西。

如果我们用 String with Int 试试,也会发生同样的事情。

scala> val int = "I'm an Int".asInstanceOf[String with Int]
int: String with Int = I'm an Int

scala> int % 2
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

String with Int 将愉快地存在,直到现实出现,我们意识到它实际上不是 Int,并且没有 % 方法。

对@m-z 解释为什么 .asInstanceOf[String with Void] 不抛出的答案的轻微扩展。我们需要区分什么存在于 Scala 中,什么存在于 JVM 字节码中。特别是,字节码不支持像 String with Void 这样的交集类型,因此不能转换为它。因此 .asInstanceOf[String with Void] 发出的字节码实际上与 .asInstanceOf[String] 相同;但是当编译器看到这种类型的值用作 Void 时,它会插入额外的转换。

这与使用 isInstanceOf 无法区分具有不同参数(如 List[Int]List[String])的泛型类型的原因相同:就字节码而言,类型相同。