是否可以在Scala中更改基类/特征的方差?

时间:2011-08-17 18:44:33

标签: scala variance

我想从Scala的不可变Map中派生出来。它定义如下:

trait Map[A, +B]

不幸的是,我的实现需要在B中保持不变。我尝试了以下内容,但没有成功:

def +(kv : (A, B)) : MyMap[A, B] = { ... }

override def +[B1 >: B](kv : (A, B1)) : MyMap[A, B1] =
    throw new IllegalArgumentException()

也许@uncheckedVariance有一个技巧?

2 个答案:

答案 0 :(得分:3)

问题在于,如果从不可变映射中派生出不变版本,则会破坏类型安全性。例如:

val dm = DiotMap(1 -> "abc")
val m: Map[Int, Any] = dm

此声明有效,因为Map是协变的。如果您的收藏无法处理协方差,那么当我使用m时会发生什么?

答案 1 :(得分:1)

完全摆脱协方差当然是不健全的,是不允许的。 鉴于m: Map[A, String]v : Any,您可以执行val mm : Map[A, Any] = m + v。这是Map定义所说的,所有实现者都必须遵循。您的类可能是不变的,但它必须实现Map的完整协变接口。

现在重新定义+以抛出错误是一个不同的故事(还不是很健全)。新的+方法存在的问题是,在进行泛型擦除后,它与其他+方法具有相同的签名。有一个技巧:添加隐式参数,以便签名中有两个参数,这使得它与第一个不同。

def +(kv : (A,B))(implicit useless: A <:< A) : MyMap[A,B]

(只要找到一个隐式参数并不重要。implicit useless: Ordering[String]同样适用。

这样做,你有过载的常见问题。如果在没有编译器知道的情况下添加B,则将调用失败的方法。最好在那里执行类型检查,以便接受任何B实例。这需要在地图中获得清单[B]。