Можно ли изменить дисперсию базового класса / черты в Scala? - PullRequest
6 голосов
/ 17 августа 2011

Я хотел бы получить от неизменной Карты Скалы. Это определяется как:

trait Map[A, +B]

К сожалению, моя реализация должна быть инвариантной в B. Я попробовал следующее, но безуспешно:

def +(kv : (A, B)) : MyMap[A, B] = { ... }

override def +[B1 >: B](kv : (A, B1)) : MyMap[A, B1] =
    throw new IllegalArgumentException()

Может быть, есть хитрость с @uncheckedVariance?

Ответы [ 2 ]

3 голосов
/ 18 августа 2011

Проблема в том, что если вы извлекаете инвариантную версию из неизменяемой карты, вы нарушите безопасность типов.Например:

val dm = DiotMap(1 -> "abc")
val m: Map[Int, Any] = dm

Это объявление действительно, поскольку Map является ковариантным.Если ваша коллекция не может обработать ковариацию, что произойдет, когда я использую m?

1 голос
/ 18 августа 2011

Полное избавление от ковариации, конечно, было бы неразумно и не допускается.Учитывая m: Map[A, String] и v : Any, вы можете сделать val mm : Map[A, Any] = m + v.Это то, что говорит определение Map, и все разработчики должны следовать.Ваш класс может быть инвариантным, но он должен реализовывать полный ковариантный интерфейс Map.

Теперь переопределение + для выдачи ошибки - это отдельная история (пока не очень хорошая).Проблема с вашим новым методом + заключается в том, что после стирания обобщенных элементов он имеет ту же сигнатуру, что и другой метод +.Есть хитрость: добавьте неявный параметр, чтобы у вас было два параметра в сигнатуре, что отличает его от первого.

def +(kv : (A,B))(implicit useless: A <:< A) : MyMap[A,B]

(не имеет значения, какой неявный параметр вы ищете, пока он найден. implicit useless: Ordering[String] работает так же хорошо)

Делая это, выесть обычная проблема с перегрузкой.Если вы добавите B без компилятора, который знает, что это так, будет вызван ошибочный метод.Там может быть лучше выполнить проверку типа, чтобы экземпляры B принимались как угодно.Для этого потребуется получить Манифест [B] на вашей карте.

...