Getting started with Scala--21st Section type parameter (iii)-Covariance and contravariance

Source: Internet
Author: User

Rocking Teen Dream
Video Address: HTTP://WWW.XUETUWUYOU.COM/COURSE/12

The main contents of this section
    1. Co-change
    2. Inverse change
    3. Type pass-by character
1. Co-change

Covariant definition forms such as: trait List[+t] {}. When the type S is a subtype of type A, then List[s] can also be considered a subtype of list[a}, i.e. List[s] can be generalized to list[a]. That is, the generalization direction of the parameterized type is consistent with the direction of the parameter type, so called covariant (covariance).


Figure 1 Covariance

For the convenience of our understanding, we first analyze why there is no covariance in the Java language and the inverse of the next sectionto. The following Java code proves that there is no covariance in Java:

java.util.List<String> s1=new LinkedList<String>();        java.util.List<Object> s2=new LinkedList<Object>();             //下面这条语句会报错        //Type mismatch: cannot convert from        // List<String> to List<Object>        s2=s1;

Although in the class hierarchy, string is a subclass of the object class, but list is not a subclass of list, which means it is not covariant. Is Java's flexibility so bad? The fact that Java does not provide covariant and contravariant features is justified because covariance and contravariance can destroy type safety. Assuming that the code above in Java is legal, we can now completely s2.add (the new person ("Rocking Teenage Dream") to add the person object to the collection, but at this point we know that S2 has pointed to S1, and that the element type in S1 is a string type. At this point, the type safety is destroyed, from this point of view, Java does not provide covariance and contravariance is reasonable.

The Scala language provides more flexibility than the Java language, which is the same as Java when you do not specify covariance and contravariance, for example:

//define your own list class class List[T](Val head:t, Val tail:list[t])  Object nonvariance {  defMain (args:array[string]): Unit = {//Compile error  //type mismatch; found:  //cn.scala.xtwy.covariance.list[string] Required:  //cn.scala.xtwy.covariance.list[any]  //note:string <: Any, but class List  //is invariant in type T.  //you wish to define T as +t instead. (SLS 4.5)   Vallist:list[any]=NewList[string] ("Rocking Teenage Dream",NULL)    }}

As you can see, when you do not specify a class as covariant, but rather a common Scala class, it is as type-safe as Java, which is called a non-variable (nonvariance) class. Scala's flexibility is that it provides the features of covariant and contravariant languages for you to choose from. The above code to make it legal, you can define the list class is covariant, the generic parameter is preceded by the + symbol, at this time the list is covariant, that is, if T is the subtype of S, that list[t] is also the subtype of List[s]. The code is as follows:

//用+标识泛型T,表示List类具有协变性class List[+T](val head: T, val tail: List[T]) object NonVariance {  def main(args: Array[String]): Unit = {   val list:List[Any]= new List[String]("摇摆少年梦",null)    

The code above will list[+t] meet the covariant requirements, but you will encounter problems adding methods to the list class with the following code:

class List[+T](val head: T, val tail: List[T]) {  //下面的方法编译会出错  //covariant type T occurs in contravariant position in type T of value newHead  //编译器提示协变类型T出现在逆变的位置  //即泛型T定义为协变之后,泛型便不能直接  //应用于成员方法当中  def prepend(newHead:T):List[T]=new List(newHead,this)}object Covariance {  def main(args: Array[String]): Unit = {   val list:List[Any]= new List[String]("摇摆少年梦",null)    

What if we define its member method? The Member method must also be defined as generic, with the following code:

class List[+T](val head: T, val tail: List[T]) {  //将函数也用泛型表示  //因为是协变的,输入的类型必须是T的超类  def prepend[U>:T](newHead:U):List[U]=new List(newHead,this)  override def toString()=""+head}object Covariance {  def main(args: Array[String]): Unit = {   val list:List[Any]= new List[String]("摇摆少年梦",null)     println(list)  
2. Inverter

contravariant definition form such as: Trait List[+t] {}
When the type S is a subtype of type A, then Queue[a] can, in turn, be considered a subtype of queue[s}. That is, the generalization direction of the parameterized type is opposite to the direction of the parameter type, so it is called inversion (contravariance). The following code gives the difference between contravariance and covariance when defining member functions:

图2 逆变//声明逆变class Person2[-A]{def test(x:A){} }//声明协变,但会报错//covariant type A occurs in contravariant position in type A of value xclass Person3[+A]{def test(x:A){} }

To understand the rationale behind it, first understand what the covariant point (covariant position) and the contravariant point (contravariant position) are.

Figure 2 Covariant points

Fig. 3 Inversion Point
Let's assume that we class Person3[+A]{ def test(x:A){} } can compile through, for Person3[anyref] and Person3[string] These two parent-child types, their test methods have the following form:

//person3[anyref]def Test (X:ANYREF) {} //person3[string ]def Test (X:string ) {}   

Because Anyref is a String-type parent class, because the type parameter a in Person3 is covariant, that is, Person3[anyref] is the parent of person3[string], so if Val panyref=new Person3 is defined [Anyref], Val pstring=new person3[string], calling Panyref.test (123) is legal, but if the panyref=pstring is re-assigned (this is legal, because the parent class can point to the child class, Also known as the Richter substitution principle), it is illegal to call Panyref.test (123) At this time, because subtypes do not accept arguments that are not string-type. That is what the parent can do, the subclass is not necessarily able to do it, the subclass is only partially satisfied.
to meet the Richter substitution principle, the function arguments in the subclass must be superclass of the function arguments in the parent class, so that the subclasses can do the same. Therefore, you need to declare the generic parameters in the class as either contravariant or invariant. class person2[-a]{def test (x:a) {}} , we can parse the Person2 and declare two variables: Val panyref=new person2[anyref], Val Pstring=new Person2[string], because it is contravariant, so person2[string] is the super class of Person2[anyref], Panyref can be assigned to pstring, Thus pstring can invoke a broader range of function parameters (such as the Pstring.test ("123") function argument can only be a string type before it is assigned, PANYREF can be assigned to pstring after it can call Test (x: ANYREF) function, which allows the function to accept a wider range of parameter types. The position of the method parameter is called the contravariant point (contravariant position), which is why Class person3[+a]{def test (x:a) {}} will cause an error. For the class person3[+a]{def test (x:a) {}} to be legal, you can use the Nether for generic qualification, such as:

class Person3[+A]{ def test[R>:A](x:R){} }

Expands the range of parameters to accept a wider range of parameter types.

With the description above, we figure out what the contravariant point is, and now let's look at what the covariant point is, and see the following code:

//下面这行代码能够正确运行class Person4[+A]{   def test:A=null.asInstanceOf[A]}//下面这行代码会编译出错//contravariant type A occurs //in covariant position in type ? A of method testclass Person5[-A]{   def test:A=null.asInstanceOf[A]}

We can also explain this by the Richter substitution principle.

scala>  class  person  [+a ] { def  F (): A=null . Asinstanceof[a]} Defined class  person   scala> val  p1=new  Person[anyref] () P1:PERSON[ANYREF] = [Email protected]8  dbd21scala> val  p2=new  person[string] () p2:person[string] = [Email protected]1  bb8caescala> p1.fres0:AnyRef = null  scala> p2.fres1:String = null   

As you can see, the parent class is more widely processed when it is defined as covariant, and the child has a relatively small processing range, which is exactly the opposite if the covariance is defined.

3. Type wildcard characters

A type wildcard is when used without specifying that it belongs to a class, but only knowing its approximate type range, through "_ <:" To achieve the purpose of type-wildcard, as in the following code

 class person(val name:string){  Override defToString () =name} class Student(name:string) extends person(name)  class Teacher(name:string) extends person(name)  class Pair[T](Val first:t,val second:t){  Override defToString () ="First:"+first+"Second:"+second;} Object typewildcard extends App {  the type parameter of//pair is limited to [_<:P Erson], that is, the input class is person and its subclasses  The //type wildcard is not the same as the generic definition, generics are used when the class is defined, and the type can be used when using a class  defMakefriends (p:pair[_<:P Erson]) ={println (P.first +"is making friend with"+ P.second)} makefriends (NewPair (NewStudent ("John"),NewTeacher ("Rocking Teenage Dream")))}

Add a public number to find out more about the latest spark and Scala technical information

Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

Getting started with Scala--21st Section type parameter (iii)-Covariance and contravariance

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.