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