我不明白Scala 删除了哪些泛型类型参数。我曾经认为它应该擦除所有泛型类型参数,但事实并非如此。
如果我错了,请纠正我:如果我在代码中实例化一个 type 的实例Map[Int, String]
,那么在运行时,该实例只知道它是 type Map[_, _]
,并且对它的泛型类型参数一无所知。这就是为什么以下成功编译和执行而没有错误的原因:
val x: Map[Int, String] = Map(2 -> "a")
val y: Map[String, Int] = x.asInstanceOf[Map[String, Int]]
现在我希望所有更高种类的类型参数也被删除,也就是说,如果我有一个
class Foo[H[_, _], X, Y]
我希望类型的实例Foo[Map, Int, String]
对Map
. 但现在考虑以下类型转换“实验”的序列:
import scala.language.higherKinds
def cast1[A](a: Any): A = a.asInstanceOf[A]
def cast2[H[_, _], A, B](a: Any) = a.asInstanceOf[H[A, B]]
def cast3[F[_[_, _], _, _], H[_, _], X, Y](a: Any) = a.asInstanceOf[F[H, X, Y]]
class CastTo[H[_, _], A, B] {
def cast(x: Any): H[A, B] = x.asInstanceOf[H[A, B]]
}
ignoreException {
val v1 = cast1[String](List[Int](1, 2, 3))
// throws ClassCastException
}
ignoreException {
val v2 = cast2[Map, Int, Long](Map[String, Double]("a" -> 1.0))
// doesn't complain at all, `A` and `B` erased
}
ignoreException {
// right kind
val v3 = cast2[Map, Int, Long]((x: Int) => x.toLong)
// throws ClassCastException
}
ignoreException {
// wrong kind
val v4 = cast2[Map, Int, Long]("wrong kind")
// throws ClassCastException
}
ignoreException {
class Foo[H[_, _], X, Y](h: H[X, Y])
val x = new Foo[Function, Int, String](n => "#" * n)
val v5 = cast3[Foo, Map, Int, Long](x)
// nothing happens, happily replaces `Function` by `Map`
}
ignoreException {
val v6 = (new CastTo[Map, Int, Long]).cast(List("hello?"))
// throws ClassCastException
}
ignoreException {
val castToMap = new CastTo[Map, Int, Long]
val v7 = castToMap.cast("how can it detect this?")
// throws ClassCastException
}
ignoreException {
val castToMap = new CastTo[Map, Int, Long]
val madCast = castToMap.asInstanceOf[CastTo[Function, Float, Double]]
val v8 = madCast.cast("what does it detect at all?")
// String cannot be cast to Function???
// Why does it retain any information about `Function` here?
}
// --------------------------------------------------------------------
var ignoreBlockCounter = 0
/** Executes piece of code,
* catches an exeption (if one is thrown),
* prints number of `ignoreException`-wrapped block,
* prints name of the exception.
*/
def ignoreException[U](f: => U): Unit = {
ignoreBlockCounter += 1
try {
f
} catch {
case e: Exception =>
println("[" + ignoreBlockCounter + "]" + e)
}
}
这是输出(scala -version 2.12.4):
[1]java.lang.ClassCastException: scala.collection.immutable.$colon$colon cannot be cast to java.lang.String
[3]java.lang.ClassCastException: Main$$anon$1$$Lambda$143/1744347043 cannot be cast to scala.collection.immutable.Map
[4]java.lang.ClassCastException: java.lang.String cannot be cast to scala.collection.immutable.Map
[6]java.lang.ClassCastException: scala.collection.immutable.$colon$colon cannot be cast to scala.collection.immutable.Map
[7]java.lang.ClassCastException: java.lang.String cannot be cast to scala.collection.immutable.Map
[8]java.lang.ClassCastException: java.lang.String cannot be cast to scala.Function1
- 案例 1、3、4 表示
asInstanceOf[Foo[...]]
确实关心Foo
,这是意料之中的。 - 情况2 表示
asInstanceOf[Foo[X,Y]]
不关心和,这也是意料之中的。X
Y
- 情况5 表示
asInstanceOf
不关心更高种类的类型参数,与情况 2 类似,这也是意料之中的。Map
到目前为止,一切都很好。但是,案例 6、7、8 提出了不同的行为:在这里,类型的实例似乎出于某种原因CastTo[Foo, X, Y]
保留了有关泛型类型参数的信息。Foo
更准确地说, aCastTo[Map, Int, Long]
似乎携带了足够的信息来知道字符串不能转换为 a Map
。此外,在案例 8 中,它似乎甚至因为演员阵容Map
而改变。Function
问题):
- 我的理解是否正确,第一个通用参数
CastTo
没有被删除,或者还有其他我看不到的东西?一些隐式操作还是什么? - 是否有任何文档描述了这种行为?
- 我有什么理由想要这种行为吗?我觉得这有点违反直觉,但也许只是我,我使用错误的工具......
谢谢阅读。
编辑:在类似的例子中四处寻找揭示了 2.12.4 编译器的问题(见下面我自己的“答案”),但这是一个单独的问题。