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