2011-08-17 82 views
6

我想从Scala的不可变Map导出。它被定义为这样的:是否有可能改变Scala中基类/特征的方差?

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一招?

+1

也许你可以使用会员地图,而不是派生? – Owen

+0

另外,为什么它需要是不变的?我认为(我不太了解斯卡拉),唯一需要不变的时候就是它既可以是源代码也可以是接收器,但是映射是不可变的,因此它不可能是一个汇。 – Owen

+0

我想实现一个双向地图。如果我实现Map,并没有什么大不了的,只是委托给定义前向和后向映射的两个内部映射,但在这种情况下我需要不变。 –

回答

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实例就可以被接受。这需要在你的地图中获得一个Manifest [B]。

3

麻烦的是,如果你从一个不可变的地图派生出一个不变的版本,你就会破坏类型安全。例如:

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

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

相关问题