Scala特征及其方法的参数化

时间:2012-02-03 11:57:38

标签: scala methods type-parameter traits

在我的应用程序中,我想使用这样的特性:

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的子类的任何类的实例。但是,在添加和删除方法上,编译器都抱怨要在“类型不匹配”的项目中添加或删除项目;找到:item.type(带底层类型A)必需:A“。我该怎么理解这个?这里我的错误是什么?该怎么做?

2 个答案:

答案 0 :(得分: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来调用它,那么一切都会很开心。但是,当您发现仅应包含Int的缓冲区现在包含Double时,您可能不会感到高兴。

答案 1 :(得分:4)

问题是你已经为另一个类型参数A定义了添加和删除方法,虽然它具有相同的名称,但它实际上是一个新的类型参数。

这应该让你前进:

def add(item: A)
def remove(item: A)
编辑:弗兰克是对的,我忘了处理BufferA协变的事实,原始声明中A明显处于逆变位置。因此,上述仅是OP问题的部分解决方案。