case class C[T](x:T) { def f(t:T) = println(t) type ValueType = T } val list = List(1 -> C(2),"hello" -> C("goodbye")) for ((a,b) <- list) { b.f(a) }
在这个例子中,我知道(运行时保证)一个类型将是一些T,而b将具有相同T的类型C [T].当然,编译器不知道,因此我们得到一个输入错误BF(a)中.
要告诉编译器这个调用是OK的,我们需要做一个类型转换àla b.f(a.asInstanceOf [T]).不幸的是,T在这里是不知道的.所以我的问题是:如何重写b.f(a)以使此代码编译?
我正在寻找一个不涉及复杂构造(保持代码可读)的解决方案,而且这是一个“干净”的意思,我们不应该依赖代码删除来使其工作(请参阅下面的第一种方法).
我试过的方法
b.asInstanceOf[C[Any]].f(a)
这是有用的,并且是可读的,但它是基于“谎言”. b不是C [Any]类型,我们没有得到运行时错误的唯一原因是因为我们依赖于JVM的限制(类型擦除).我认为只有使用x.asInstanceOf [X]才是很好的风格,当我们知道x真的是X型.
b.f(a.asInstanceOf[b.ValueType])
这应该是根据我对类型系统的理解而工作的.我已经将成员ValueType添加到C类,以便能够明确地引用类型参数T.然而,在这种方法中我们得到一个神秘的错误信息:
Error:(9,22) type mismatch; found : b.ValueType (which expands to) _1 required: _1 b.f(a.asInstanceOf[b.ValueType]) ^
为什么?似乎抱怨我们期望类型_1,但得到类型_1! (但是即使这种方法有效,它也限于我们有可能将成员ValueType添加到C的情况.如果C是一些现有的库类,那么我们也不能这样做).
for ((a,b) <- list.asInstanceOf[List[(T,C[T]) forSome {type T}]]) { b.f(a) }
这一个工作,并且语义正确(即,当调用asInstanceOf时,我们不会“说谎”).限制是这有点不可读.此外,它对当前情况有些具体:如果a,b不是来自同一个迭代器,那么我们可以在哪里应用此类型转换? (此代码也对Intelli / J IDEA 2016.2过于复杂的副作用,它将其显示为编辑器中的错误.)
val (a2,b2) = (a,b).asInstanceOf[(T,C[T]) forSome {type T}] b2.f(a2)
我会期望这个工作,因为a2,b2现在应该有类型T和C [T]相同的存在T.但我们得到一个编译错误:
Error:(10,9) type mismatch; found : a2.type (with underlying type Any) required: T b2.f(a2) ^
为什么? (除此之外,这种方法的缺点是由于一对的创建和销毁而导致运行时费用(我认为).)
b match { case b : C[t] => b.f(a.asInstanceOf[t]) }
解决方法
for ((a,b: C[t]) <- list) { b.f(a.asInstanceOf[t]) }
小提琴:http://www.scala-js-fiddle.com/gist/b9030033133ee94e8c18ad772f3461a0
如果你不是在理解,不幸的是,相应的模式分配不起作用:
val (c,d: C[t]) = (a,b) d.f(c.asInstanceOf[t])
这是因为在第二行,t不再是范围了.在这种情况下,您必须使用完整的模式匹配.