Почему параметр находится в контравариантном положении? - PullRequest
31 голосов
/ 08 марта 2012

Я пытаюсь использовать параметр ковариантного типа внутри черты для создания класса case, например так:

trait MyTrait[+T] {
  private case class MyClass(c: T)
}

компилятор говорит:

error: covariant type T occurs in contravariant position in type T of value c

Затем я попробовалследующее, но это также не сработало:

trait MyTrait[+T] {
  private case class MyClass[U <: T](c: U)
}

ошибка на этот раз:

error: covariant type T occurs in contravariant position in type >: Nothing <: T of type U

Может кто-нибудь объяснить, почему T находится здесь в ковариантном положении, и предложить решение дляЭта проблема?Thx!

Ответы [ 2 ]

61 голосов
/ 08 марта 2012

Это фундаментальная особенность объектно-ориентированного программирования, которая не получает столько внимания, сколько заслуживает.

Предположим, у вас есть коллекция C[+T].+T означает, что если U <: T, то C[U] <: C[T].Справедливо.Но что значит быть подклассом?Это означает, что каждый метод должен работать , который работал в исходном классе.Итак, предположим, у вас есть метод m(t: T).Это говорит о том, что вы можете взять любой t и сделать что-нибудь с ним.Но C[U] может делать что-то только с U, что может быть не все из T!Таким образом, вы сразу же опровергли свое утверждение о том, что C[U] является подклассом C[T].Это не .Есть вещи, которые вы можете сделать с C[T], которые вы не можете сделать с C[U].

Теперь, как вы можете обойти это?

Один из вариантов - сделатьинвариант класса (отбросить +).Другой вариант - если вы берете параметр метода, чтобы разрешить любой суперкласс : m[S >: T](s: S).Теперь, если T изменится на U, это не страшно: суперкласс T также является суперклассом U, и метод будет работать.(Однако затем вы должны изменить свой метод, чтобы иметь возможность обрабатывать такие вещи.)

С классом case сделать его еще сложнее, если вы не сделаете его инвариантным.Я рекомендую делать это и распространять дженерики и дисперсию в другом месте.Но мне нужно увидеть больше деталей, чтобы убедиться, что это подойдет для вашего варианта использования.

13 голосов
/ 08 марта 2012

Почти нет. Здесь:

scala> trait MyTrait[+T] {
     |   private case class MyClass[U >: T](c: U)
     | }
defined trait MyTrait

Что означает MyClass[Any] действительно для всех T. Именно в этом причина того, что нельзя использовать T в этой позиции, но для демонстрации этого требуется больше кода, чем я сейчас нахожусь в настроении. : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...