В своем приложении я хочу использовать такую черту:
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". Как я должен это понимать? В чем здесь моя ошибка и что делать?
Ответ 1 · рейтинг 5 · Frank
Вы параметризуете методы другим параметром типа 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, все будет хорошо. Вы, вероятно, не будете счастливы позже, когда обнаружите, что ваш буфер, который должен содержать только Ints, теперь содержит Double.
Ответ 2 · рейтинг 4 · fotNelton
Проблема в том, что вы определили методы добавления и удаления по отношению к другому параметру типа A, который, хотя и имеет такое же имя, на самом деле является параметром нового типа.
Это должно вас заинтересовать:
def add(item: A)
def remove(item: A)
РЕДАКТИРОВАТЬ: Фрэнк прав, я забыл иметь дело с тем фактом, что Buffer является ковариантным в A, где в исходном объявлении A явно находится в контравариантном положении. Следовательно, вышеизложенное является лишь частичным решением проблемы ОП.