泛型類
- 在類聲明時(shí)春畔,定義一些泛型類型蹂匹,然后在類的內(nèi)部,就可以使用這些泛型類型
- 在需要對(duì)類中的某些成員蚤认,如字段或方法中的參數(shù)進(jìn)行統(tǒng)一的類型限制時(shí)米苹,可以使用泛型類,使得程序具有更好的健壯性和穩(wěn)定性
- 在使用類的時(shí)候砰琢,將類型參數(shù)替換為實(shí)際的類型即可
- scala會(huì)自動(dòng)推斷泛型類型:給泛型類型的字段賦值時(shí)蘸嘶,scala會(huì)自動(dòng)對(duì)類型進(jìn)行推斷
泛型函數(shù):
- 與泛型類相似,在聲明函數(shù)時(shí)指定泛型類型陪汽,然后在函數(shù)體內(nèi)训唱,多個(gè)變量或返回值,就可以使用泛型類型進(jìn)行聲明挚冤。
- 可以通過(guò)給使用了泛型類型的變量傳遞值雪情,讓scala自動(dòng)推斷泛型的實(shí)際類型,也可以在調(diào)用函數(shù)的時(shí)候你辣,手動(dòng)指定泛型的實(shí)際類型
class Triple[X, Y, Z](val first: X, val second: Y, val thrid: Z)
object Hello_Type_Parameterization {
def main(args: Array[String]): Unit = {
//在定義后scala的類型推斷會(huì)得出triple類型為 Triple[String, Int, Double]
val triple = new Triple("Spark", 3, 3.1415926)
//顯示聲明類型
val bigData = new Triple[String, String, Char]("Spark", "Hadoop", 'R')
//定義泛型函數(shù)
def getData[T](list: List[T]) = list(list.length / 2)
println(getData(List("Spark", "Hadoop", 'R'))) //Hadoop
//顯式指定類型
val f = getData[Int] _ //val f: List[Int] => Int
println(f(List(1,2,3,4,5,6,7,8))) //5
//定義參數(shù)也存在上下文的約束
def foo[A, B](f: A => List[A], b: A) = f(b)
}
}
類型變量的邊界(Bounds)
- <: :指明上界巡通,表達(dá)了泛型的類型必須是"某種類型"或某種類型的"子類"
- '>: :指明下界,表達(dá)了泛型的類型必須是"某種類型"或某種類型的"父類"
/**
* 這里的[T <: Comparable[T]] 表示類型T必須是Comparable[T]的子類
* 如果T為Comparable[T]的子類了舍哄,那么T一定會(huì)有compareTo這個(gè)方法宴凉,這是一個(gè)java的方法
*/
class Pair[T <: Comparable[T]](val first: T, val second: T) {
def bigger = if(first.compareTo(second) > 0) first else second
}
object Type_Variable_Bounds {
def main(args: Array[String]): Unit = {
val pair = new Pair("Spark", "Hadoop")
println(pair.bigger) //Spark
}
}
視圖界定 View Bounds
- view bounds其實(shí)就是bounds 上邊界的加強(qiáng)版本,對(duì)bounds的補(bǔ)充 <變成<%
- 可以利用implicit隱式轉(zhuǎn)換將實(shí)參類型轉(zhuǎn)換成目標(biāo)類型
/**
當(dāng)給下面這個(gè)類傳入3表悬、5時(shí)會(huì)報(bào)錯(cuò)弥锄。因?yàn)?、5不是Comparable[T]的子類
class Pair_NotPerfect[T <: Comparable[T]](val first: T, val second: T) {
def bigger = if (first.compareTo(second) > 0) first else second
*/
/*
*將<:改成 <%就是視圖界定 這樣就可以傳遞3和5了,就不會(huì)報(bào)錯(cuò)了
*我們可以把傳入的T類型的實(shí)例隱式的轉(zhuǎn)換成Comparable[T]類型
*/
class Pair_NotPerfect[T <% Comparable[T]](val first: T, val second: T) {
def bigger = if (first.compareTo(second) > 0) first else second
}
Ordered視圖界定
/**
*
*上面這種方式的12行first.compareTo(second) > 0 通過(guò)compareTo來(lái)比較 但是不能直觀的像數(shù)學(xué)比較那樣清晰
* Scala提供了Ordered視圖界定籽暇,Ordered在Comparable上提供一些關(guān)系型的操作符 < > <= >=等
*
*/
class Pair_Batter[T <% Ordered[T]](val first: T, val second: T) {
//這里的 > 是因?yàn)镺rdered中提供的方法
def bigger = if (first > second) first else second
}
object View_Bounds {
def main(args: Array[String]): Unit = {
var pair = new Pair_NotPerfect("Spark", "Hadoop")
println(pair.bigger) //Spark
/*
* 當(dāng)類型界定為Pair_NotPerfect[T <: Comparable[T]]報(bào)錯(cuò) 因?yàn)镮nt本身不是Comparable的子類
*
* 當(dāng)類型界定為視圖界定時(shí)Pair_NotPerfect[T <% Comparable[T]] 就可以正常運(yùn)行
* 是因?yàn)镮nt本身不是Comparable的子類型 Scala通過(guò)"隱式轉(zhuǎn)換"將Int轉(zhuǎn)換成RichInt 而這個(gè)類型是Comparable的子類
*/
var pairInt = new Pair_NotPerfect(3, 5) //Int -> RichInt
println(pairInt.bigger) //5
/**
* 注意:這樣定義不是因?yàn)镾tring的上界是Ordered[String]
* 當(dāng)使用視圖界定時(shí) 會(huì)發(fā)生"隱式轉(zhuǎn)換" 把String --> RichString
* 而RichString是Ordered[RichString]的子類型 RichString中是實(shí)現(xiàn)了這樣的 < > <= >=等方法
* 從而真正是讓String類型完成視圖界定
*/
var pair_Batter_String = new Pair_Batter("Java", "Scala")
println(pair_Batter_String.bigger) //Scala
val pair_Batter_Int = new Pair_Batter(20, 12)
println(pair_Batter_Int.bigger) //20
}
}
上下文界定Context Bounds
- 上下文界定[T : Ordering]温治,這種寫法在Spark中是廣泛使用的,說(shuō)明存在一個(gè)隱式的值Ordering[T]
- implicit ordered: Ordering[T]
class Pair_Ordering[T : Ordering] (val first: T, val second: T) {
//這是一個(gè)隱式轉(zhuǎn)換的顯式定義戒悠,這個(gè)函數(shù)沒(méi)有參數(shù)熬荆,當(dāng)時(shí)函數(shù)執(zhí)行的時(shí)候 這個(gè)隱式值就會(huì)自動(dòng)傳進(jìn)來(lái)
def bigger(implicit ordered: Ordering[T]) = {
if (ordered.compare(first, second) > 0) first else second
}
}
object Context_Bounds {
def main(args: Array[String]): Unit = {
val pair = new Pair_Ordering("Spark", "Hadoop")
println(pair.bigger) //Spark
val pairInt = new Pair_Ordering(3, 5)
println(pairInt.bigger) //5
}
}
ClassTag和Manifest
- 上下文界定[T : ClassTag]:相當(dāng)于動(dòng)態(tài)類型,記錄了當(dāng)前T的類型绸狐,你使用時(shí)傳入什么類型就是什么類型卤恳,在實(shí)際運(yùn)行的時(shí)候我們獲取T具體的類型
- 主要是應(yīng)用于創(chuàng)建泛型數(shù)組,因?yàn)閿?shù)組必須有具體的類型寒矿,否則無(wú)法創(chuàng)建相應(yīng)的數(shù)組突琳,利用[T : ClassTag]就可以創(chuàng)建成功
import scala.reflect.ClassTag
object Manifest_ClassTag {
def main(args: Array[String]): Unit = {
/**
* Q: 可以創(chuàng)建泛型數(shù)組嗎?
*理論上是不可以的,因?yàn)闆](méi)有指定具體的符相,在Scala程序運(yùn)行中拆融,數(shù)組必須有具體的類型,沒(méi)有否無(wú)法創(chuàng)建的相應(yīng)的數(shù)組
*引出Manifest的概念可以創(chuàng)建泛型數(shù)組
*[T : Manifest]這樣的寫法被稱之為Manifest上下文界定 實(shí)質(zhì)上這是需要一個(gè)Manifest[T]類型的隱式對(duì)象 這又是一個(gè)"隱式轉(zhuǎn)換"的過(guò)程啊终,有這樣的一個(gè)隱式轉(zhuǎn)換來(lái)輔助我們構(gòu)建Manifest[T]來(lái)確定T的類型
* 通過(guò)這個(gè)隱式的值來(lái)輔助構(gòu)建泛型數(shù)組冠息,來(lái)確定T的具體類型
* 所以在創(chuàng)建泛型函數(shù)時(shí) 需要Manifest的類型來(lái)輔助構(gòu)建泛型數(shù)組,借助Manifest類型對(duì)象來(lái)指定泛型數(shù)組具體的類型
*
* 通過(guò)Manifest[T]可以記錄T的類型 在實(shí)際運(yùn)行的時(shí)候我們獲取T具體的類型
* */
def arrayMake[T : Manifest](first: T, second: T) = {
val r = new Array[T](2)
r(0) = first
r(1) = second
r
}
arrayMake(1, 2).foreach(println) //1 2
/**
* Manifest的原生寫法 不推薦
*/
def manif[T](x: List[T])(implicit m: Manifest[T]) = {
if (m <:< manifest[String]) //<:< 表示 m是manifest[String]類型
println("List Strings")
else
println("Some other type")
}
manif(List("Spark", "Hadoop")) //List Strings
manif(List(1, 2)) //Some other type
manif(List("Scala", 3)) //Some other type
/**
* [T : ClassTag]這種寫法說(shuō)明:當(dāng)這個(gè)函數(shù)在運(yùn)行時(shí)時(shí) 對(duì)存在一個(gè)ClassTag[T]一個(gè)隱式值 這種方式是最常用的
主要是在運(yùn)行時(shí)指定孕索,在編譯時(shí)無(wú)法確定的type的信息
編寫編譯的時(shí)候沒(méi)有具體類型,運(yùn)行的時(shí)候必須要有具體的類型躏碳,所以需要一種機(jī)制運(yùn)行的時(shí)候會(huì)根據(jù)類型進(jìn)行推斷類型搞旭,classTag會(huì)幫我們存儲(chǔ)這個(gè)類的信息,然后交給虛擬機(jī)
*/
def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*)
mkArray(42, 13).foreach(println) //42 13
mkArray("Japan", "Brazil", "Germany").foreach(println) //"Japan", "Brazil", "Germany"
}
}
Scala多重界定
- T <: A with B:T是A或者B的子類
- T >: A with B:A或者B是T的子類
- T >: A <: B:T同時(shí)擁有下界A和上界B(也就是說(shuō)A必為B的子類型菇绵,下界必須寫在前面肄渗,上界必須寫在后面)
- T : A : B : 上下文界定
- T <% A <% B:視圖界定,T可以同時(shí)擁有多個(gè)視圖界定咬最,必須能夠同時(shí)轉(zhuǎn)化為A和B的要求
Scala類型約束
/**
* A =:= B 表示A類型等同于B類型
* A <:< B 表示A類型是B類型的子類型
*/
object Type_Contraints {
def main(args: Array[String]): Unit = {
//隱式參數(shù)是從哪里傳進(jìn)來(lái)的翎嫡?后面有一些列的判斷
def rocky[T](i: T)(implicit ev: T <:< java.io.Serializable) {
println(ev) //<function1>
println("Life is short, you need Spark!")
}
rocky("spark")
//rocky(100) error:Cannot prove that Int <:< java.io.Serializable.
}
}
總結(jié)
-
邊界(Bounds)
- [T <: Comparable[T]]
- <: :指明上界,表達(dá)了泛型的類型必須是"某種類型"或某種類型的"子類"
- '>: :指明下界永乌,表達(dá)了泛型的類型必須是"某種類型"或某種類型的"父類"
-
視圖界定 View Bounds
- [T <% Comparable[T]]
- <% : 對(duì)上邊界的加強(qiáng)版惑申,可以利用implicit隱式轉(zhuǎn)換將實(shí)參類型轉(zhuǎn)換成目標(biāo)類型
-
上下文界定Context Bounds
- [T : Ordering]
- 說(shuō)明存在一個(gè)隱式的值Ordering[T](implicit ordered: Ordering[T])
- 針對(duì)創(chuàng)建泛型數(shù)組的上下文界定:
- [T : ClassTag]
- [T : Manifest]