2012-02-03 3 views
3

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

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 ». Как я должен это понимать? Какая моя ошибка здесь и что делать?

ответ

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.

+0

Да, я должен признать, что это объяснение очень детализировано и, таким образом, будет направлением для тех, кто может столкнуться с подобной проблемой. Что касается Scala-компилятора - слишком плохо, что он допускает такие эквивалентные именованные типы аппараметров. Было бы логично запретить их полностью. Поскольку никто не хотел бы называть его параметры типа подобными, и те, кто хотел бы это сделать, просто объявили бы плохой стиль кодирования. Обфускация кода - это боковая область, она не должна отображаться в обычном программировании. – noncom

4

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

Это должно вас происходит:

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

EDIT: Фрэнк прав, я забыл иметь дело с тем, что Buffer ковариантен в A, где в оригинале декларации, A явно в контравариантном должность. Следовательно, вышеизложенное является лишь частичным решением проблемы ОП.

+0

Очень хороший ответ, thaks! – noncom

+0

-1. хотя ОП принял его, это не решает проблему использования ковариантного типа в контравариантной позиции. Или простыми словами: он не будет компилироваться только с этим изменением. – Frank