Каково другое использование дисперсии типа, кроме параметров ввода/вывода?
Я понимаю, что дисперсия типа используется в следующих случаях:
-
Если общий тип 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 с помощью нижней границы: (ковариация) и верхней границы < (:) (контравариантность). Это можно увидеть в следующем фрагменте кода. Это из моего собственного 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)