Каково другое использование дисперсии типа, кроме параметров ввода/вывода?

Я понимаю, что дисперсия типа используется в следующих случаях:

  • Если общий тип G имеет параметр типа T1, который появляется как тип аргумента метода G, тогда G может быть контравариантным в T1.

  • Если G имеет параметр типа T2, который появляется как тип любого возвращаемого значения метода (или ctor) G, то G может быть ковариантным в T2.

Что делать, если я могу заменить, должен быть в предложениях выше? Есть ли другой случай совместного и контра-варианта использования? Когда и почему вы делаете свои типы совпадающими и противоречивыми?

Ответ 1

Цитата из спецификации, раздел 4.5 Различия Аннотации:

Аннотации вариаций показывают, как экземпляры параметризованных типов меняются в зависимости от к подтипированию (п. 3.5.2). Дисперсия "+" указывает на ковариантную зависимость, a - дисперсия указывает на контравариантную зависимость и отсутствующую индикацию дисперсии указывает на инвариантную зависимость. Аннотации дисперсии ограничивают способ отображения переменной аннотированного типа в типе или классе, который связывает параметр типа. В определении типа тип T [tps] = S или тип объявления типа T [tps] > : параметры типа L <: U помеченный + 'должен появляться только в ковариантной позиции, тогда как параметры типа, помеченные '-' должен появляться только в контравариантном положении.

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


scala> class A[+T, -S] {def myMethod(s: String) = println(s)}
defined class A

scala> class A2[T] {def myMethod(t: T) = println(t)}
defined class A2

scala> class A3[-T] {def myMethod(t: T) = println(t)}
defined class A3

scala> val a1 = new A2[Any]
a1: A2[Any] = [email protected]

scala> val a2: A2[Int] = a1
:6: error: type mismatch;
 found   : A2[Any]
 required: A2[Int]
       val a2: A2[Int] = new A2[Any]

scala> val a3  = new A3[Any]
a3: A3[Any] = [email protected]

scala> val a4: A3[Int] = a3
a5: A3[Int] = [email protected]

Аннотации дисперсии в классе A3, которая является контравариантной в этом примере, делает, что A3 [Any] считается подтипом A3 [Int], делая назначение от экземпляра a4 до a3 законным. Это не удается, если вы не используете аннотацию отклонения.

Ответ 2

Все просто не так просто. Иногда дисперсия не имеет никакого смысла, поэтому вы просто сохраняете инвариант класса.

Также обратите внимание, что дисперсия переключается по цепочке использования. Например:

class A[+T]
class B[-T] {
  def f(x: A[T]) {}
}

class C[+T] {
  def g(x: B[T]) {}
}

Или, по-другому, это непростая вещь, которую можно описать несколькими строками. Какова главная причина, почему сильное применение дисперсии по Scala является очень полезной вещью - в настоящее время я наполовину убежден, что большинство кода, использующих дисперсию в Java, должны иметь тонкие ошибки.

Ответ 3

Позвольте мне попробовать этот старый вопрос. Одним из способов использования ковариации и контравариантности является некоторое ограничение на Generic с помощью нижней границы: (ковариация) и верхней границы &lt (:) (контравариантность). Это можно увидеть в следующем фрагменте кода. Это из моего собственного blog по этому вопросу.

    abstract class Animal (animalType:String)

    class HasFourLegs(animalType:String) extends Animal(animalType){
      def move=println(this+" walking on four legs")
    }

    class HasTwoLegs(animalType:String) extends Animal(animalType){
      def move=println(this+" walking on Two legs")
    }

    case class Dog(animalType:String) extends HasFourLegs(animalType)
    case class Ostrich(animalType:String) extends HasTwoLegs(animalType)

      def moveOn4legs[T<:HasFourLegs](animal:T)=  animal.move
      val dog = Dog("dog")
      val ostrich=Ostrich("ostrich")
      moveOn4legs(dog)
      /*
      moveOn4legs(ostrich)
      error: inferred type arguments [this.Ostrich] do not conform to method moveOn4legs type parameter bounds [T <: this.HasFourLegs]
      moveOn4legs(ostrich)
      ^
      */
      println

    class AnimalMovement [+T]{
      def movement[U>:T](animal:U)=println(animal+" walking on Two legs!!!")
    }

      val moveLikeTwoLegs=new AnimalMovement[HasTwoLegs]()
      moveLikeTwoLegs.movement(ostrich)
      moveLikeTwoLegs.movement(dog)