scala type variable

Type variable is the correlation subtyping of subtype relations thereto complex type component type. Scala supports  generic class  type parameters of type variable annotation, allowing them to be covariant inverter or without the use of annotations is constant. In the type using variable type system allows us to establish a visual connection between the complex type, and variations will be deficient abstract restricted reuse.

class Foo[+A] // A covariant class
class Bar[-A] // A contravariant class
class Baz[A]  // An invariant class

Covariant

Use annotations  +Acan make a generic class type parameter  A becomes covariant. For some classes  class List[+A], so  A become covariant means that for both types  A and  B, if  A a  B sub-type, then  List[A] that is  List[B] a sub-type. This allows us to use generics to create a very useful and intuitive sub-type relationship.

Consider the following simple class structure:

abstract class Animal {
  def name: String
}
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal

Type  Cat and  Dog all  Animal subtypes. The library has a common Scala immutable classes  sealed abstract class List[+A], wherein the parameter type  A is covariant. This means that  List[Cat]Shi  List[Animal], List[Dog] also  List[Animal]. Intuitively, a list of cat and dog list is a list of animals is reasonable, you should be able to replace any of them with  List[Animal].

In the following example, the method of  printAnimalNames the animals receiving the list as a parameter and prints out the progressive their names. If  List[A] not covariant, the last two method calls will not be compiled, which will severely limit  printAnimalNames the applicability of the method.

object CovarianceTest extends App {
  def printAnimalNames(animals: List[Animal]): Unit = {
    animals.foreach { animal =>
      println(animal.name)
    }
  }

  val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom"))
  val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex"))

  printAnimalNames(cats)
  // Whiskers
  // Tom

  printAnimalNames(dogs)
  // Fido
  // Rex
}

Inverter

By using annotations  -A, you can make a generic class type parameter  A becomes inverter. And covariant Similarly, it will create a subtype relationship between the class and type of parameters, but the opposite effect and covariant. That is, for a class  class Writer[-A] , the  A inverter means that for both types  A and  B, if  A a  B sub-type,  Writer[B] is  Writer[A] a sub-type.

Consider the use of the following classes defined above embodiment  Cat, Dog and  Animal :

abstract class Printer[-A] {
  def print(value: A): Unit
}

Here  Printer[A] is a simple class that is used to print out a certain type  A. Let us define some specific sub-categories:

class AnimalPrinter extends Printer[Animal] {
  def print(animal: Animal): Unit =
    println("The animal's name is: " + animal.name)
}

class CatPrinter extends Printer[Cat] {
  def print(cat: Cat): Unit =
    println("The cat's name is: " + cat.name)
}

If you  Printer[Cat] know how to print out any of the console  Cat, and  Printer[Animal] know how to print out any of the console  Animal, you  Printer[Animal] should also know how to print out  Cat is reasonable. Inverse relationship does not apply, because  Printer[Cat] do not know how to print out any of the console  Animal. So, if we like, we should be able to use  Printer[Animal] the replacement  Printer[Cat], the  Printer[A] inverter allows us to do this.

object ContravarianceTest extends App {
  val myCat: Cat = Cat("Boots")

  def printMyCat(printer: Printer[Cat]): Unit = {
    printer.print(myCat)
  }

  val catPrinter: Printer[Cat] = new CatPrinter
  val animalPrinter: Printer[Animal] = new AnimalPrinter

  printMyCat(catPrinter)
  printMyCat(animalPrinter)
}

The output of this procedure are as follows:

The cat's name is: Boots
The animal's name is: Boots

constant

By default, Scala in the generic class is unchanged. This means that they are neither covariant nor contravariant. In the following example, the class  Container is constant. Container[Cat] NotContainer[Animal] vice versa.

class Container[A](value: A) {
  private var _value: A = value
  def getValue: A = _value
  def setValue(value: A): Unit = {
    _value = value
  }
}

May seem a  Container[Cat] natural and should be a  Container[Animal], but allows a variable to be covariant generic class is not safe. In this example, the Container constant is very important. Suppose  Container actually covariant, the following may occur:

val catContainer: Container[Cat] = new Container(Cat("Felix"))
val animalContainer: Container[Animal] = catContainer
animalContainer.setValue(Dog("Spot"))
val cat: Cat = catContainer.getValue // 糟糕,我们最终会将一只狗作为值分配给一只猫

Fortunately, the compiler will stop us before that.

Other examples

Another type of change can help to understand example is the Scala standard library  trait Function1[-T, +R]Function1 It represents a function having a parameter, wherein the first type parameter  T indicates the parameter type, the second type parameter  R indicates the return type. Function1 In which the parameter is an inverter type, and in the return type is covariant. For this example, we will use text symbols  A => B to represent  Function1[A, B].

Similar assumptions used previously  Cat, Dog, Animal inheritance, plus the following:

abstract class SmallAnimal extends Animal
case class Mouse(name: String) extends SmallAnimal

Suppose we are dealing with animals that received the type of function, and return to their type of food. If we want a  Cat => SmallAnimal(because the cat eat small animals), but give it a  Animal => Mouse, our program will still work. Intuitively, a  Animal => Mouse function will still accept a  Cat as an argument, because  Cat that is a  Animal, and the function returns a  Mousealso a  SmallAnimal. Since we can safely implicitly with the former instead of the latter, we can say  Animal => Mouse that  Cat => SmallAnimal subtype.

Comparison with other languages

Scala and some similar language in different ways supportive change. For example, in the type variable Scala Notes and it is very similar to C #, when defining an Abstract addition type variable annotations (dot type variable declaration). In Java, however, it is used when an Abstract (using variable dot type), will be given annotation type variable.

Guess you like

Origin www.cnblogs.com/feng9exe/p/11429512.html