Scala方差:协变,不变和逆变

时间:2020-02-23 14:41:50  来源:igfitidea点击:

在这篇文章中,我们将讨论Scala Variance及其用例。

什么是方差?

方差定义参数化类型的继承关系。
差异全部与子类型有关。

请浏览下图以了解"什么是参数化类型"。

在此,T被称为"类型参数",而List [T]被称为泛型。

对于List [T],如果我们使用List [Int],List [AnyVal]等,则这些List [Int]和List [AnyVal]被称为"参数化类型"

方差定义了这些参数化类型之间的继承关系。

Scala中的差异优势

Scala Variance的主要优点是:

  • 差异使Scala集合更具类型安全性。

  • 差异使开发更加灵活。

  • Scala Variance为我们提供了开发可靠应用程序的技术。

Scala中的方差类型

Scala支持以下三种方差。

  • 协变
  • 不变的
  • 逆变的

我们将在接下来的部分中详细讨论这三个差异。

Scala中的协变

如果" S"是" T"的子类型,则List [S]是List [T]的子类型。

双参数类型之间的这种继承关系被称为"共变"

Scala协方差语法:-为了表示两个参数化类型之间的协方差关系,Scala使用以下语法:前缀类型带" +"符号的参数定义Scala中的协方差。

T是类型参数," +"符号定义Scala协方差。

注意:-为简单起见,我在这里使用"列表"。
但是,它可以是任何有效的Scala类型,例如Set [+ T],Ordered [+ T]等。

示例:-编写一个Scala程序来演示Scala协变量子类型技术。

class Animal[+T](val animial:T)

class Dog
class Puppy extends Dog

class AnimalCarer(val dog:Animal[Dog])

object ScalaCovarianceTest{
def main(args: Array[String]) {
  val puppy = new Puppy
  val dog = new Dog

  val puppyAnimal:Animal[Puppy] = new Animal[Puppy](puppy)
  val dogAnimal:Animal[Dog] = new Animal[Dog](dog)

  val dogCarer = new AnimalCarer(dogAnimal)
  val puppyCarer = new AnimalCarer(puppyAnimal)

  println("Done.")
}
}

注意:-由于Animal类是通过使用方差注释(即" + T")定义的,因此我们可以传递dogAnimal或者其子类型puppyAnimal来创建AnimalCarer对象。

如果我们在动物类定义中删除差异注释,就像如下图所示:

class Animal[T](val animial:T)
//Remaining code is same as above.

它不会编译。
我们将收到以下编译错误消息:

Type mismatch, expected: Animal[Dog], found: Animal[Puppy]

为了解决这类问题,我们应该使用Scala协方差。

按照这个例子,我们可以说以下斯卡拉协方差:

"由于小狗是狗的亚型,所以动物[小狗]是动物[狗]的亚型。
我们可以在需要Animal [Dog]的地方使用Animal [Puppy]。
"这就是Scala协方差。

Scala中的变量

如果" S"是" T"的子类型,则List [T]是List [S]的子类型。

两种参数化类型之间的这种继承关系称为" Contravariant"

Scala变量语法:为了表示两个参数化类型之间的变量关系,Scala使用以下语法:前缀类型参数中带有"-"符号的参数定义了Scala中的变量。

示例:-编写一个Scala程序来演示Scala可变子类型技术。

abstract class Type [-T]{
def typeName : Unit
}

class SuperType extends Type[AnyVal]{
override def typeName: Unit = {
  println("SuperType")
}
}
class SubType extends Type[Int]{
override def typeName: Unit = {
  println("SubType")
}
}

class TypeCarer{
def display(t: Type[Int]){
  t.typeName
}
}

object ScalaContravarianceTest {

def main(args: Array[String]) {
  val superType = new SuperType
  val subType = new SubType

  val typeCarer = new TypeCarer

  typeCarer.display(subType)
  typeCarer.display(superType)
}

}

注意:-当我们在Type [-T]中定义Contravariance时,它工作良好。
TypeCarer.display()是使用Type Int定义的,但仍可以接受Type [AnyVal],因为Scala的Contravariance子类型化。

如果我们去掉" - "中键入比如定义"类型[T]",那么我们将得到编译错误。

Scala中的不变式

如果"S"是"T"的子类型列表然后[S]和List [T]不具有继承关系或者子类型。
这意味着两者无关。

两种参数化类型之间的这种关系称为"不变或者无变量"

在Scala中,默认情况下,泛型类型具有Non-Variant关系。
如果我们定义参数化类型,而无需使用"+"或者‘ - ’符号,则他们被称为不变。

什么是Scala中的方差注释?

差异注释表示在类型参数之前定义" +"或者"-"。

例如:+ T和–T在Scala中称为方差注释。

Scala方差摘要

在本节中,我们将总结在以上各节中讨论过的有关Scala方差类型的所有3个概念。

Scala Variance TypeSyntaxDescription
Covariant[+T]If S is subtype of T, then List[S] is also subtype of List[T]
Contravariant[-T]If S is subtype of T, then List[T] is also subtype of List[S]
Invariant[T]If S is subtype of T, then List[S] and List[T] are unrelated.