Scala入门到精通——第二十一节 类型参数(三)

  • 时间:
  • 浏览:7

通过前述的描述,我门弄明白了那先 是逆变点,现在我门来看一下那先 是协变点,先看下面的代码:

scala语言相比java语言提供了更多的灵活性,当不指定协变与逆变时,它和java是一样的,之类:

那可能性定义其成员最好的最好的法子呢?只能将成员最好的最好的法子也定义为泛型,代码如下:

还只能看后,当不指定类为协变的完后 ,可是我四个 多普通的scala类,此时它跟java一样是具有类型安全的,称这些类是非变的(Nonvariance)。scala的灵活性在于它提供了协变与逆变语言特点供你选者。上述的代码要使其合法,还只能定义List类是协变的,泛型参数前面用+符号表示,此时List可是我协变的,即可能性T是S的子类型,那List[T]也是List[S]的子类型。代码如下:

上述代码将List[+T]满足协变要求,但往List类中打上去最好的最好的法子完会遇到那先 的难题,代码如下:

协变定义形式如:trait List[+T] {} 。当类型S是类型A的子类型时,则List[S]也还只能认为是List[A}的子类型,即List[S]还只能泛化为List[A]。也可是我被参数化类型的泛化方向与参数类型的方向是一致的,可是我称为协变(covariance)。

逆变定义形式如:trait List[-T] {}

当类型S是类型A的子类型,则Queue[A]反过来还只能认为是Queue[S}的子类型。也可是我被参数化类型的泛化方向与参数类型的方向是相反的,可是我称为逆变(contravariance)。 下面的代码给出了逆变与协变在定义成员函数时的区别:



图2 逆变示意图

确随便说说类层次特征上看,String是Object类的子类,但List<String>并全部都是的List<Object>子类,也却句子它全部都是协变的。java的灵活性就这么差吗?随便说说java不提供协变和逆变这些特征是有其道理的,这是可能性协变和逆变会破坏类型安全。假设java中上面的代码是合法的,我门此时全部还只能s2.add(new Person(“摇摆少年梦”)往集合中打上去Person对象,但此时我门知道, s2可能性指向了s1,而s1上面的元素类型是String类型,这时其类型安全就被破坏了,从这些高度来看,java不提供协变和逆变是有其合理性的。

打上去公众微信号,还只能了解更多最新Spark、Scala相关技术资讯

类型通配符是位于使用时不具体指定它属于某个类,可是我只知道其大致的类型范围,通过”_ <:” 达到类型通配的目的,如下面的代码

将参数范围扩大,从而促进接受更广泛的参数类型。

为方便我门理解,我门先分析java语言中为那先 不位于协变及下一节要讲的逆变。下面的java代码证明了Java中不位于协变:

可能性AnyRef是String类型的父类,可能性Person3中的类型参数A是协变的,也即Person3[Any]是Person3[String]的父类,这些可能性定义了val pAny=new Person3[AnyRef]、val pString=new Person3[String],调用pAny.test(123)是合法的,但可能性将pAny=pString进行重新赋值(这是合法的,可能性父类还只能指向子类,也称里氏替换原则),此时再调用pAny.test(123)完后 ,这是非法的,可能性子类型不接受非String类型的参数。也可是我父类能做的事情,子类不一定能做,子类可是我帕累托图满足。

为满足里氏替换原则,子类中函数参数的只能是父类中函数参数的超类,那我句子父类能做的子类促进做。这些只能将类中的泛型参数声明为逆变或不变的。class Person2[-A]{ def test(x:A){} },我门还只能对Person2进行分析,同样声明四个 多变量:val pAnyRef=new Person2[AnyRef]、val pString=new Person2[String],可能性是逆变的,可是我Person2[String]是Person2[AnyRef]的超类,pAnyRef还只能赋值给pString,从而pString还只能调用范围更广泛的函数参数(比如未赋值完后 ,pString.test(“123”)函数参数只能为String类型,则pAnyRef赋值给pString完后 ,它还只能调用test(x:AnyRef)函数,使函数接受更广泛的参数类型。最好的最好的法子参数的位置称为做逆变点(contravariant position),这是class Person3[+A]{ def test(x:A){} }会报错的意味 。为使class Person3[+A]{ def test(x:A){} }合法,还只能利用下界进行泛型限定,如:

这里我门同样还只能通过里氏替换原则来进行说明

作者:摇摆少年梦

视频地址:http://www.xuetuwuyou.com/course/12



图1 协变示意图

还只能看后,定义为协变时父类的处里范围更广泛,而子类的处里范围相对较小;可能性定义协变句子,正好与此相反。

要理解清楚上面的原理,先要理解清楚那先 是协变点(covariant position) 和 逆变点(contravariant position)。



图2 协变点



图3 逆变点

我门先假设class Person3[+A]{ def test(x:A){} } 促进编译通过,则对于Person3[Any] 和 Person3[String] 这四个 多父子类型来说,它们的test最好的最好的法子分别具有下列形式: