Подтвердить что ты не робот

Почему параметр в контравариантном положении?

Я пытаюсь использовать параметр ковариантного типа внутри признака, чтобы построить класс 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 находится здесь в ковариантной позиции и предлагает решение этой проблемы? спасибо!

4b9b3361

Ответ 1

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

Предположим, что у вас есть коллекция 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, еще труднее получить это право, если вы не сделаете его инвариантным. Я рекомендую это сделать и выталкивать дженерики и отклонения в другом месте. Но мне нужно будет увидеть более подробную информацию, чтобы быть уверенным, что это будет работать для вашего случая использования.

Ответ 2

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

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

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