第十九章 泛型和約束系統
這章是scala的重點和難點 ,建議多找一點資料和範例理解 ,scala很多庫函數都使用了這種語法 ,瞭解後才能更好的閱讀源碼 .
package SecondWithProgrammingInScala
import java.util.Comparator
/**
* scala的泛型和約束[類型系統]
* 書上的例子比較散 ,所以參考了其他資料來了解scala這個最強大的特性
*/
//1.泛型 (類似Java)
class Reference[T] {
private var contents: T = _
def set(value: T) {
contents = value
}
def get: T = contents
}
object ReferenceApp {
def main(args: Array[String]) {
val cell = new Reference[Int]
cell.set(13)
println("Reference contains the half of " + (cell.get * 2))
val cell2 = new Reference[String]
cell2.set("33")
println("Reference contains the number " + cell2.get)
}
}
//2.類型變量界定 : 劃定一個類型的範圍
//類似java中 List<? extends Object> list = new ArrayList<String>(); ,限制類型
// <:泛型類型限定符,Comparable[T]是類型T的上界,T是Comparable[T]的下界
class Pair[T <: Comparable[T]](val first: T, val second: T) {
//表示T必須是Comparable的子類 ,可以使用compareTo方法進行比較,如果大於0返回first
def bigger = if (first.compareTo(second) > 0) first else second
}
class PairLowerBound[T](val first: T, val second: T) {
// 傳入的參數泛型T必須爲 R的子類 ,返回構造Pair_Lower_Bound對象 ,R是T的上界,T是R的下界
// new了一個父類型
def replaceFirst[R >: T](newFirst: R) = new PairLowerBound[R](newFirst, second)
}
object TypeVariableBoundsApp {
def main(args: Array[String]): Unit = {
val pair = new Pair("Spark", "Hadoop")
println(pair.bigger)
val pairLowerBound = new PairLowerBound("Int", 12)
println(pairLowerBound.first.getClass)
println(pairLowerBound.replaceFirst('I').first.getClass)
}
}
//3.視圖界定 : 自動的隱式轉換
//視圖界定(Views Bounds) ,其符號爲 T <% S,關係和上界類似
//如果類型T不是Ordered[T]的子類 ,會進行隱式轉換成Ordered的子類 ,再使用這個方法
class ViewBounds[T <% Ordered[T]](val first: T, val second: T) {
def compare = if (first > second) 1 else 0
}
class ViewBounds2[T](val first: T, val second: T)(implicit ord: T => Ordered[T]) {
def compare = if (ord(first) > second) 1 else 0
}
object ViewBoundsApp {
def main(args: Array[String]): Unit = {
//Int(沒有Ordered方法)->RichInt
val c = new ViewBounds[Int](2, 1)
println(c.compare)
}
}
//4.上下文界定 : 爲隱式參數引入的語法糖 ,使隱式轉換簡潔
object ContextBound {
//隱式參數的做法
def max1[T](a: T, b: T)(implicit cp: Comparator[T]) = {
if (cp.compare(a, b) >= 0) a else b
}
//使用上下文界定 : 將參數列表中的隱式轉換移到內部
def max2[T: Comparator](a: T, b: T) = {
//def inner(implicit c: Comparator[T]) = c.compare(a, b)
//if (inner > 0) a else b
val cp = implicitly[Comparator[T]]
if (cp.compare(a, b) > 0) a else b
}
def main(args: Array[String]): Unit = {
implicit val c = new Comparator[Int] {
override def compare(a: Int, b: Int) = a - b
}
println(max1(5, 6))
println(max2(5, 6))
}
}
//5.組合界定符
/*
表示:A和B爲T上界
T <: A with B
表示 : A和B爲T下界
T >: A with B
表示 : 同時擁有上界和下界 , 並且A爲下界 , B爲上界 , A爲B的子類 , 順序不能顛倒 。
T >: A <: B
表示 : 類型變量界定 , 即同時滿足AT這種隱式值和BT這種隱式值
T: A: B
表示 : 視圖界定 , 即同時能夠滿足隱式轉換的A和隱式轉換的B
T <% A <% B
*/
//6.泛型關鍵字
//Java中的泛型是編譯時的 ,編譯後會對泛型繼續擦除
//List<String>,List<Int>到了JVM中都是List類型 ,只是會對他加上String,Int的強制轉換
//因此如果運行時需要判斷類型 ,就要對類型參數進行保存
object KeyWord {
/**
* scala在2.10裏用TypeTag替代了Manifest,用ClassTag替代了ClassManifest
* 因爲Manifest在路徑依賴系統中有問題
* 例如 類A{類B}
* 類B的類型會隨着類A的不同實例而不同
* 然而Manifest判斷是相同的
*/
//ClassTag : 把原始類型T保存在方法上下文
//數組必須明確具體類型 ,但只有在函數運行時纔會知道類型 ,這時候泛型已經被擦除了
import scala.reflect.ClassTag
def mkArray1[T: ClassTag](elems: T*) = Array[T](elems: _*)
//TypeTag不僅包含類的類別信息,還包含了所有靜態的類信息
//在沒有路徑歧義的地方 ,使用Manifest也可以 ,官方建議替換
import scala.reflect.runtime.universe._
def matchType[T](x: List[T])(implicit tag: TypeTag[T]) = {
if (typeOf[T] =:= typeOf[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
def main(args: Array[String]): Unit = {
mkArray1(1, 2).foreach(println)
matchType(List("1"))
matchType(List(1))
//7.類型約束
// A =:=B // 表示A類型等同於B類型
// A <:<B // 表示A類型是B類型的子類
def rocky[T](i: T)(implicit ev: T <:< java.lang.String) {
println("Life is short ,you need spark!!!")
}
//rocky(1) 無法運行
rocky("Spark")
}
}
//8.協變和逆變
/**
* covariant協變 : C[+T]:如果A是B的子類,那麼C[A]是C[B]的子類。
* contravariance逆變 : C[-T]:如果A是B的子類,那麼C[B]是C[A]的子類。
* Invariance不變(默認) : 只能使用原始類型 . C[T]:無論A和B是什麼關係,C[A]和C[B]沒有從屬關係。
*/
//父類
class Person
//子類
class Student extends Person
//協變父類
class C[+T](val args: T)
//協變子類 繼承協變類型
class S[+T](args: T) extends C[T](args)
object CovariantApp {
def main(args: Array[String]): Unit = {
//創建泛型爲Student類的類C
val child = new S[Student](new Student)
//因爲開啓了協變 ,S[Person]是S[Student]的父類
val parent: S[Person] = child
//同樣C是S的父類 ,C[Person]也是S[Student]的父類
val parent1: C[Person] = child
}
}