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

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

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

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

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

Ответы [ 3 ]

3 голосов
/ 12 марта 2011

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

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

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

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

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

3 голосов
/ 11 марта 2011

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

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

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


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до а3 легальный.Это не удастся, если вы не используете аннотацию отклонений.

1 голос
/ 15 декабря 2011

Позвольте мне попробовать этот старый вопрос. Одно из применений ковариации и контравариантности заключается в том, чтобы иметь некоторое ограничение на Обобщение посредством нижней границы>: (ковариация) и верхней границы <: (контравариантность). Использование можно увидеть в следующем фрагменте кода. Это из моего <a href="http://blog.aunndroid.com/2011/12/learning-scala-upper-bound-for.html" rel="nofollow"> блога на эту тему.

    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)
...