考虑以下示例:
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)
}
在这个例子中,我知道(运行时保证)类型a
将会有一些T
, and b
将有类型C[T]
与相同的T
。当然,编译器无法知道这一点,因此我们会得到一个输入错误b.f(a)
.
为了告诉编译器这个调用没问题,我们需要进行类型转换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
to C
. If 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
and 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])
}
这有效。但是用匹配项括住代码会使代码的可读性大大降低。 (而且对于 Intelli/J 来说也太复杂了。)