Scala 的类型擦除如何用于更高种类的类型参数?
How does Scala's type erasure work for higher kinded type parameters?
我不明白Scala 删除了哪些 泛型类型参数。以前觉得应该把all个泛型类型参数抹掉,但是好像不是这样
如果我错了请指正:如果我在代码中实例化了一个 Map[Int, String]
类型的实例,那么在运行时,该实例只知道它是 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
一无所知。但现在考虑以下类型转换序列 "experiments":
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$$Lambda3/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
不 关心更高种类的类型参数 Map
,与案例 2 类似,这也是预期的。
到目前为止一切顺利。但是,案例 6、7、8 表明了不同的行为:在这里,类型 CastTo[Foo, X, Y]
的实例似乎出于某种原因保留了有关泛型类型参数 Foo
的信息。更准确地说,CastTo[Map, Int, Long]
似乎携带了足够的信息,可以知道字符串不能转换为 Map
。此外,在案例 8 中,它似乎甚至因为转换而将 Map
更改为 Function
。
问题:
-
CastTo
的第一个通用参数没有被删除,我的理解是否正确,或者还有其他我没有看到的东西?一些隐式操作或什么?
- 是否有描述此行为的文档?
- 我有什么理由想要这种行为吗?我发现它有点违反直觉,但也许只有我一个人,我用错了工具...
感谢阅读。
编辑:在类似的示例中四处寻找发现了 2.12.4 编译器的问题(参见下面我自己的 "answer"),但这是一个单独的问题.
好的,这可能不是预期的行为。
这是上面的随机版本:
ignoreException {
class Foo[X, Y]
class Bar[X, Y]
val castToMap = new CastTo[Map, Int, Long]
val madderCast = if (math.random() > 0.5) {
println("Foo")
castToMap.asInstanceOf[CastTo[Foo, Float, Double]]
} else {
println("Bar")
castToMap.asInstanceOf[CastTo[Bar, String, Any]]
}
val v9 = madderCast.cast("crash 'dis serva, awww yeah!")
// crashes the compile server for scala 2.12.4
// dotty 0.4.0-RC1 doesn't complain at all (as expected!)
}
2.12.4 编译器崩溃。 Dotty 没有问题。
编辑 更短、独立的示例:
进口scala.language.higherKinds
class Foo[X]
class Bar[X]
class CrashIt[A[_]] {
def cast(a: Any): A[Int] = a.asInstanceOf[A[Int]]
}
val c = if (true) new CrashIt[Foo] else new CrashIt[Bar]
val x = c.cast("")
崩溃:
java.util.NoSuchElementException: head of empty list
at scala.collection.immutable.Nil$.head(List.scala:428)
at scala.collection.immutable.Nil$.head(List.scala:425)
at scala.tools.nsc.typechecker.ContextErrors$InferencerContextErrors$InferErrorGen$.
NotWithinBoundsErrorMessage(ContextErrors.scala:1045)
at scala.tools.nsc.typechecker.ContextErrors$InferencerContextErrors$InferErrorGen$.
NotWithinBoundsContextErrors.scala:1052)
at scala.tools.nsc.typechecker.Infer$Inferencer.issueBoundsError(Infer.scala:881)
at scala.tools.nsc.typechecker.Infer$Inferencer.check(Infer.scala:887)
at scala.tools.nsc.typechecker.Infer$Inferencer.checkBounds(Infer.scala:891)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.checkBounds(RefChecks.scala:1203)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.checkTypeRef(RefChecks.scala:1384)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transform(RefChecks.scala:1668)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transform$adapted(RefChecks.scala:1659)
at scala.reflect.internal.tpe.TypeMaps$ForEachTypeTraverser.traverse(TypeMaps.scala:1102)
at scala.reflect.internal.Types$Type.foreach(Types.scala:787)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1407)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1369)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1368)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1186)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.itransform(Trees.scala:1416)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1420)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1419)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1198)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.itransform(Trees.scala:1378)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1375)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1373)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1198)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.itransform(Trees.scala:1416)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1425)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1424)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1198)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1438)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1438)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:140)
at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$applyPhase(Global.scala:436)
at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:429)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$run(Global.scala:400)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$adapted(Global.scala:400)
at scala.collection.Iterator.foreach(Iterator.scala:929)
at scala.collection.Iterator.foreach$(Iterator.scala:929)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1417)
at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:400)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1452)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1436)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1429)
at scala.tools.nsc.Global$Run.compile(Global.scala:1545)
at scala.tools.nsc.StandardCompileServer.session(CompileServer.scala:150)
at scala.tools.util.SocketServer.$anonfun$doSession(SocketServer.scala:80)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withOut(Console.scala:163)
at scala.tools.util.SocketServer.$anonfun$doSession(SocketServer.scala:80)
at scala.tools.util.SocketServer.$anonfun$doSession$adapted(SocketServer.scala:75)
at scala.tools.nsc.io.Socket.applyReaderAndWriter(Socket.scala:49)
at scala.tools.util.SocketServer.doSession(SocketServer.scala:75)
at scala.tools.util.SocketServer.loop(SocketServer.scala:91)
at scala.tools.util.SocketServer.run(SocketServer.scala:103)
at scala.tools.nsc.CompileServer$.$anonfun$execute(CompileServer.scala:216)
at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.java:12)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withOut(Console.scala:163)
at scala.tools.nsc.CompileServer$.$anonfun$execute(CompileServer.scala:211)
at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.java:12)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withErr(Console.scala:192)
at scala.tools.nsc.CompileServer$.main(CompileServer.scala:211)
at scala.tools.nsc.CompileServer.main(CompileServer.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at scala.reflect.internal.util.ScalaClassLoader.$anonfun$run(ScalaClassLoader.scala:99)
at scala.reflect.internal.util.ScalaClassLoader.asContext(ScalaClassLoader.scala:34)
at scala.reflect.internal.util.ScalaClassLoader.asContext$(ScalaClassLoader.scala:30)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:125)
at scala.reflect.internal.util.ScalaClassLoader.run(ScalaClassLoader.scala:99)
at scala.reflect.internal.util.ScalaClassLoader.run$(ScalaClassLoader.scala:91)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:125)
at scala.tools.nsc.CommonRunner.run(ObjectRunner.scala:22)
at scala.tools.nsc.CommonRunner.run$(ObjectRunner.scala:21)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
at scala.tools.nsc.CommonRunner.runAndCatch(ObjectRunner.scala:29)
at scala.tools.nsc.CommonRunner.runAndCatch$(ObjectRunner.scala:28)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
at scala.tools.nsc.MainGenericRunner.runTarget(MainGenericRunner.scala:66)
at scala.tools.nsc.MainGenericRunner.run(MainGenericRunner.scala:85)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:101)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
铸造步骤似乎很关键。如果删除 cast
的调用,则不会发生任何事情。
如果参数 A
不是更高级的,则不会发生这种情况,因此例如对于 class CrashIt[A]
和 new CrashIt[Int]
编译器不会报错。
EDIT-2:找不到这个确切的错误(或明显的概括),在此处报告:https://github.com/scala/bug/issues/10700 .
我认为你混淆了一些东西。
对通用类型的转换被推迟到类型变得具体的时候。例如,拿这段代码:
class CastTo[H[_, _], A, B] {
def cast(x: Any): H[A, B] = x.asInstanceOf[H[A, B]]
}
在字节码中你只能转换为真正的 class,因为它对泛型一无所知。所以上面的内容,在字节码中,大致等同于:
class CastTo {
def cast(x: Object): Object = x
}
然后在后面的代码中你给方法cast
一个String
,编译器可以看到根据它拥有的类型信息,一个Map[Int, Long]
会出来。但是在字节码 cast
中有一个被擦除的 return 类型的 Object
,所以编译器必须在 use-site 的cast
方法。此代码
val castToMap = new CastTo[Map, Int, Long]
val v7 = castToMap.cast("how can it detect this?")
将在字节码中大致等同于以下(伪)代码:
val castToMap = new CastTo
val v7 = castToMap.cast("how can it detect this?").asInstanceOf[Map]
关于您的其他问题:
- 不是我立即知道的。
- 你为什么不想要它?您正在将
String
转换为 Map[Int, Long]
。那最终肯定会崩溃。使用 ClassCastException
快速(相对)失败可能是最安全、对用户最友好的选择。
我不明白Scala 删除了哪些 泛型类型参数。以前觉得应该把all个泛型类型参数抹掉,但是好像不是这样
如果我错了请指正:如果我在代码中实例化了一个 Map[Int, String]
类型的实例,那么在运行时,该实例只知道它是 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
一无所知。但现在考虑以下类型转换序列 "experiments":
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$$Lambda3/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
不 关心更高种类的类型参数Map
,与案例 2 类似,这也是预期的。
到目前为止一切顺利。但是,案例 6、7、8 表明了不同的行为:在这里,类型 CastTo[Foo, X, Y]
的实例似乎出于某种原因保留了有关泛型类型参数 Foo
的信息。更准确地说,CastTo[Map, Int, Long]
似乎携带了足够的信息,可以知道字符串不能转换为 Map
。此外,在案例 8 中,它似乎甚至因为转换而将 Map
更改为 Function
。
问题:
-
CastTo
的第一个通用参数没有被删除,我的理解是否正确,或者还有其他我没有看到的东西?一些隐式操作或什么? - 是否有描述此行为的文档?
- 我有什么理由想要这种行为吗?我发现它有点违反直觉,但也许只有我一个人,我用错了工具...
感谢阅读。
编辑:在类似的示例中四处寻找发现了 2.12.4 编译器的问题(参见下面我自己的 "answer"),但这是一个单独的问题.
好的,这可能不是预期的行为。
这是上面的随机版本:
ignoreException {
class Foo[X, Y]
class Bar[X, Y]
val castToMap = new CastTo[Map, Int, Long]
val madderCast = if (math.random() > 0.5) {
println("Foo")
castToMap.asInstanceOf[CastTo[Foo, Float, Double]]
} else {
println("Bar")
castToMap.asInstanceOf[CastTo[Bar, String, Any]]
}
val v9 = madderCast.cast("crash 'dis serva, awww yeah!")
// crashes the compile server for scala 2.12.4
// dotty 0.4.0-RC1 doesn't complain at all (as expected!)
}
2.12.4 编译器崩溃。 Dotty 没有问题。
编辑 更短、独立的示例:
进口scala.language.higherKinds
class Foo[X]
class Bar[X]
class CrashIt[A[_]] {
def cast(a: Any): A[Int] = a.asInstanceOf[A[Int]]
}
val c = if (true) new CrashIt[Foo] else new CrashIt[Bar]
val x = c.cast("")
崩溃:
java.util.NoSuchElementException: head of empty list
at scala.collection.immutable.Nil$.head(List.scala:428)
at scala.collection.immutable.Nil$.head(List.scala:425)
at scala.tools.nsc.typechecker.ContextErrors$InferencerContextErrors$InferErrorGen$.
NotWithinBoundsErrorMessage(ContextErrors.scala:1045)
at scala.tools.nsc.typechecker.ContextErrors$InferencerContextErrors$InferErrorGen$.
NotWithinBoundsContextErrors.scala:1052)
at scala.tools.nsc.typechecker.Infer$Inferencer.issueBoundsError(Infer.scala:881)
at scala.tools.nsc.typechecker.Infer$Inferencer.check(Infer.scala:887)
at scala.tools.nsc.typechecker.Infer$Inferencer.checkBounds(Infer.scala:891)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.checkBounds(RefChecks.scala:1203)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.checkTypeRef(RefChecks.scala:1384)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transform(RefChecks.scala:1668)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transform$adapted(RefChecks.scala:1659)
at scala.reflect.internal.tpe.TypeMaps$ForEachTypeTraverser.traverse(TypeMaps.scala:1102)
at scala.reflect.internal.Types$Type.foreach(Types.scala:787)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1407)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1369)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1368)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1186)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.itransform(Trees.scala:1416)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1420)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1419)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1198)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.itransform(Trees.scala:1378)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1375)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1373)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1198)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.itransform(Trees.scala:1416)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1425)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1424)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1198)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.$anonfun$transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1169)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:107)
at scala.reflect.internal.Trees.$anonfun$itransform(Trees.scala:1438)
at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
at scala.reflect.internal.Trees.itransform(Trees.scala:1438)
at scala.reflect.internal.Trees.itransform$(Trees.scala:1348)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1751)
at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:107)
at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:140)
at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$applyPhase(Global.scala:436)
at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:429)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$run(Global.scala:400)
at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$adapted(Global.scala:400)
at scala.collection.Iterator.foreach(Iterator.scala:929)
at scala.collection.Iterator.foreach$(Iterator.scala:929)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1417)
at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:400)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1452)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1436)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1429)
at scala.tools.nsc.Global$Run.compile(Global.scala:1545)
at scala.tools.nsc.StandardCompileServer.session(CompileServer.scala:150)
at scala.tools.util.SocketServer.$anonfun$doSession(SocketServer.scala:80)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withOut(Console.scala:163)
at scala.tools.util.SocketServer.$anonfun$doSession(SocketServer.scala:80)
at scala.tools.util.SocketServer.$anonfun$doSession$adapted(SocketServer.scala:75)
at scala.tools.nsc.io.Socket.applyReaderAndWriter(Socket.scala:49)
at scala.tools.util.SocketServer.doSession(SocketServer.scala:75)
at scala.tools.util.SocketServer.loop(SocketServer.scala:91)
at scala.tools.util.SocketServer.run(SocketServer.scala:103)
at scala.tools.nsc.CompileServer$.$anonfun$execute(CompileServer.scala:216)
at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.java:12)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withOut(Console.scala:163)
at scala.tools.nsc.CompileServer$.$anonfun$execute(CompileServer.scala:211)
at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.java:12)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
at scala.Console$.withErr(Console.scala:192)
at scala.tools.nsc.CompileServer$.main(CompileServer.scala:211)
at scala.tools.nsc.CompileServer.main(CompileServer.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at scala.reflect.internal.util.ScalaClassLoader.$anonfun$run(ScalaClassLoader.scala:99)
at scala.reflect.internal.util.ScalaClassLoader.asContext(ScalaClassLoader.scala:34)
at scala.reflect.internal.util.ScalaClassLoader.asContext$(ScalaClassLoader.scala:30)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:125)
at scala.reflect.internal.util.ScalaClassLoader.run(ScalaClassLoader.scala:99)
at scala.reflect.internal.util.ScalaClassLoader.run$(ScalaClassLoader.scala:91)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:125)
at scala.tools.nsc.CommonRunner.run(ObjectRunner.scala:22)
at scala.tools.nsc.CommonRunner.run$(ObjectRunner.scala:21)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
at scala.tools.nsc.CommonRunner.runAndCatch(ObjectRunner.scala:29)
at scala.tools.nsc.CommonRunner.runAndCatch$(ObjectRunner.scala:28)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
at scala.tools.nsc.MainGenericRunner.runTarget(MainGenericRunner.scala:66)
at scala.tools.nsc.MainGenericRunner.run(MainGenericRunner.scala:85)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:101)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
铸造步骤似乎很关键。如果删除 cast
的调用,则不会发生任何事情。
如果参数 A
不是更高级的,则不会发生这种情况,因此例如对于 class CrashIt[A]
和 new CrashIt[Int]
编译器不会报错。
EDIT-2:找不到这个确切的错误(或明显的概括),在此处报告:https://github.com/scala/bug/issues/10700 .
我认为你混淆了一些东西。
对通用类型的转换被推迟到类型变得具体的时候。例如,拿这段代码:
class CastTo[H[_, _], A, B] {
def cast(x: Any): H[A, B] = x.asInstanceOf[H[A, B]]
}
在字节码中你只能转换为真正的 class,因为它对泛型一无所知。所以上面的内容,在字节码中,大致等同于:
class CastTo {
def cast(x: Object): Object = x
}
然后在后面的代码中你给方法cast
一个String
,编译器可以看到根据它拥有的类型信息,一个Map[Int, Long]
会出来。但是在字节码 cast
中有一个被擦除的 return 类型的 Object
,所以编译器必须在 use-site 的cast
方法。此代码
val castToMap = new CastTo[Map, Int, Long]
val v7 = castToMap.cast("how can it detect this?")
将在字节码中大致等同于以下(伪)代码:
val castToMap = new CastTo
val v7 = castToMap.cast("how can it detect this?").asInstanceOf[Map]
关于您的其他问题:
- 不是我立即知道的。
- 你为什么不想要它?您正在将
String
转换为Map[Int, Long]
。那最终肯定会崩溃。使用ClassCastException
快速(相对)失败可能是最安全、对用户最友好的选择。