Черта Скалы и параметризация ее методов - PullRequest
3 голосов
/ 03 февраля 2012

В моем приложении я хочу использовать такую ​​черту:

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 }
}

Классы, которые наследуют эту черту, должны иметь возможность буферизовать любые экземпляры классов, которые являются дочерними для класса А. Однако в обоих случаях добавлениеи удалите методы, которые компилятор жалуется на элемент, добавляемый или удаляемый из элементов, которые «несоответствие типов; найдено: item.type (с базовым типом A) требуется: A».Как я должен это понимать?В чем здесь моя ошибка и что делать?

Ответы [ 2 ]

5 голосов
/ 03 февраля 2012

Вы параметризуете методы с другим параметром типа 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 s, теперь содержит Double.

4 голосов
/ 03 февраля 2012

Проблема в том, что вы определили методы добавления и удаления для другого параметра типа A, который, хотя и имеет то же имя, действительно является параметром нового типа.

Это должно помочь вам:

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

РЕДАКТИРОВАТЬ: Фрэнк прав, я забыл иметь дело с тем фактом, что Buffer является ковариантным в A, где в исходном объявлении, A явно в противоположной позиции. Следовательно, вышеизложенное является лишь частичным решением проблемы ОП.

...