2012-02-03 72 views
3

在我的应用我想用这样的特点:斯卡拉特质及其方法参数化

trait HasBuffer[+A] { 

    var items = Buffer[A]() 

    def add[A](item: A) { items += item } 
    def remove[A](item: A) { items -= item } 
    def set(is: Buffer[A]) { items = is } 
    def clear { items clear } 
} 

类继承这一特点应该能够缓冲的谁是A类的子类的任何实例然而上添加和移除这两种方法的编译器抱怨被添加或删除的项目从项“类型不匹配;实测值:项目。形式(与下面的A型)需要:A”。我应该如何理解这一点?我在这里犯了什么错误,该怎么办?

回答

5

您正在使用参数化从类定义的一个不同的另一种类型的参数A方法。这里是你用改名参数写的版本:

trait HasBuffer[+A] { 

    var items = Buffer[A]() 

    def add[B](item: B) = items += item 
    def remove[B](item: B) { items -= item } 
    def set(is: Buffer[A]) { items = is } 
    def clear { items clear } 
} 

现在应该清楚,为什么编译器会拒绝这个。

相反,你可以简单地写这样的方法:

def add(item: A) = items += item 
def remove(item: A) { items -= item } 

但是,你仍然会收到编译器错误指出协变型A的禁忌和不变的位置出现。

协变的一点是,如果你期望HasBuffer[AnyVal]可以通过HasBuffer[Int]。但是,如果您希望AnyVal,并使用该类型的add方法为好,你就可以到一个完全不同类型添加到您的HasBuffer[Int]。因此,编译器拒绝这个。

相反,你必须提供一个下界类型参数是这样的:

trait HasBuffer[+A, V <: A] { 

    var items = Buffer[V]() 

    def add(item: V) = items += item 
    def remove(item: V) { items -= item } 
    def set(is: Buffer[V]) { items = is } 
    def clear { items clear } 
} 

有了这个,你现在可以有一个像下面的方法:

def doSomething[X <: AnyVal](b : HasBuffer[AnyVal, X], e : X) = b.add(e) 

这种方法将工作在各种满足所需下限的HasBuffer类型参数组合上。

精神上比较这与没有提供一个下界的想法。然后,该方法会成为这样的事情:

// doesn't make much sense! 
def doSomething(b : HasBuffer[AnyVal], e : AnyVal) = b.add(e) 

如果调用此方法HasBuffer[Int]类型的对象和Double这将是所有快乐。你可能会不高兴lateron不过,当你发现你的缓冲区应该只包含Int小号现在包含一个Double

+0

是的,我不得不承认这个解释是非常详细的,因此是一个可能遇到类似麻烦的人的方向。至于Scala编译器 - 太糟糕了,它确实允许这种等价命名的类型参数。完全禁止它们是合乎逻辑的。由于没有人愿意将他的类型参数命名为类似这样的类型参数以及那些想要这样做的类型参数,因此只会声明不好的样式编码。代码混淆是一个侧面,它不应该出现在纯编程中。 – noncom 2012-02-03 13:42:35

4

的问题是,你所定义的添加和移除相对于其他类型的参数A,虽然它具有相同的名称,确实是一个新的类型参数的方法。

这应该让你去:

def add(item: A) 
def remove(item: A) 

编辑:弗兰克是正确的,我忘了处理的事实,BufferA协变如在原有声明,A显然是一个逆变位置。因此,以上只是解决OP问题的部分解决方案。

+0

一个非常好的答案,泰克! – noncom 2012-02-03 12:28:03

+0

-1。即使OP接受它,但这并不能解决协变类型在逆变位置中使用的问题。或者用更简单的话来说:它不会随着这种变化而编译。 – Frank 2012-02-03 13:08:03