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

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

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

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

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

3 ответа

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

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

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

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

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

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

Примечания об отклонениях показывают, как экземпляры параметризованных типов варьируются в зависимости от подтипа (§3.5.2). Дисперсия "+" указывает ковариантную зависимость, дисперсия "-" указывает контравариантную зависимость, а указание отсутствующей дисперсии указывает инвариантную зависимость. Отклонение от аннотации ограничивает способ отображения аннотированной переменной типа в типе или классе, который связывает параметр типа. В типе определения типа 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] = A2@1cd1cea

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] = A3@875dee

scala> val a4: A3[Int] = a3
a5: A3[Int] = A3@875dee

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

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

    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's 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)
Другие вопросы по тегам