国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

scala 協變和逆變 在函數上的應用

2019-11-08 19:40:37
字體:
來源:轉載
供稿:網友

假設A <: B, 可以簡單認為A是B的子類(A <: B的具體定義是:任何B滿足的性質,A都滿足):

A<:B的前提下,有如下對協變逆變的定義:

C[A] <: C[B]                                     C 是 協變的 covariantC[A] >: C[B]                                    C 是 逆變的 contravariantC[A]和C[B]都不是彼此的子類    C是 非變的 nonvariant

C是AnyRef類,即引用類,例如Int是值類,List則是引用類,這是因為List需要定義其元素的類型,比如List[Int]

C[A]代表C的元素是A類對象,C[A]<:C[B]代表C[B]是C[A]基類,因此C[B]可以賦值給C[A]

Scala可以用下面的方式更簡單聲明容器類的性質

Class C[+A] {..}                                           C 是 協變的 covariantClass C[-A] {…}                                           C 是 逆變的 contravariantClass C[A]{…}                                     C是 非變的 nonvariant

例子

 

Trait List[+T]{ Def perpend(elem:T):List[T] = new Cons(elem, this)}假設基類Inset有兩個子類nonEmpty和empty

這個定義是錯誤的,當objectA 是List[nonempty], 語句:objectA.PRepend(empty) 會導致類型匹配錯誤。

 

prepend是屬于trait List[+T]的方法

(這里List[+T]的用處是,當有對象A為List[NonEmpty], 注意是List對象不是NonEmpty對象,則可以把對象A賦值給一個需要List[Inset]的參數,因為List類是協變的------nonEmpty是Inset子類,List[NonEmpty]便是List[Inset]的子類)

出現錯誤的情形:

假設此時this是一個List[nonempty]的對象,

則List[T](List[+T]賦予List的性質在這里不用考慮了),編譯器會識別T是nonEmpty類,所以如果this.prepend(empty)會導致類型錯誤,因為empty和nonEmpty都是InSet的子類,但是empty不是nonEmpty的子類,不能賦值給需要nonEmpty的參數位置(elem:T)。

正確的定義方式:

Def perpend[U>:T](elem: U) :List[U] =new Cons(elem, this)

U>:T 表示:U是T的基類,T是U的子類。因此原先List[nonEmpty]的對象,T是nonEmpty,但是U是T的基類,所以U是Inset。傳入prepend方法的元素要求是Inset類型,empty是Inset子類當然可以傳入,得到的new Cons(empty, this)是List[Inset],毫無問題

 

應用

 

 

1 ).選擇支持協變的容器類:

1. Inset class 中有NoEmpty 和 empty兩個子類。

2. Array不是協變,即定義abstractclass Array[T] extends Seq[T]。

3. 如果Array換成List, 因為定義abstractclass List[+T] extends Seq[T],List是協變,則NonEmpty是Inset的子類,List[NonEmpty]是List[Inset]的子類,第二行的a可以賦值給b。類似于c++的多態,子類指針和子類引用是可以分別賦值給基類指針和基類引用

 

2). 定義新的函數對象,例如

object addOne extends Function1[Int,Int]{  def apply(m:Int):Int= m + 1}利用函數的協變:

If A2 <: A1 并且 B1<:B2 則有:

 A1=> B1 <: A2 => B2

因此scala中的Function1特性的定義如下:

Package scala

Trait Function1[-T, +U] {  def apply(x:T) : U

}

假設有兩個Function1:

 

scala> val f1: Int => String = x=> s"Int($x)"

f1: Int => String = <function1>

 

scala> val f2: Any => String = x=> s"Any($x)"

f2: Any => String = <function1>

 

凡是f1可以使用的地方,f2都是可以用的(考慮基類子類的關系:基類可以使用的場景,子類都可以使用);但反過來不行。

所以類型Function1[Any, String]應該是Function1[Int, String]的子類, 是Trait Function1[-T, +U]

 的定義賦予了Function1類更豐富的繼承關系。

逆變的傳入類型,允許子類在重寫基類的函數時,傳入參數的類型比基類原函數定義的傳入參數類型更廣泛。

而協變的返回類型,允許了子類在重寫基類的函數時,可以返回比基類原函數定義的返回類型更加具體的類型。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 高碑店市| 临潭县| 万盛区| 咸宁市| 广州市| 红桥区| 南汇区| 都昌县| 英德市| 县级市| 灵丘县| 乐清市| 建水县| 东乌| 东光县| 富平县| 棋牌| 凤冈县| 上饶县| 莱西市| 琼结县| 岑溪市| 衡阳县| 顺昌县| 黑河市| 兴仁县| 陇西县| 文登市| 扎囊县| 邯郸县| 韶山市| 上高县| 长乐市| 昌邑市| 铜山县| 昭通市| 阳新县| 文安县| 临泉县| 扶绥县| 札达县|