一文詳解scala泛型及型別限定
今天知識星球球友,微信問浪尖了一個spark原始碼閱讀中的型別限定問題。這個在spark原始碼很多處出現,所以今天浪尖就整理一下scala型別限定的內容。希望對大家有幫助。
scala型別引數要點1. 非變
trait Queue[T] {}
這是非變情況。這種情況下,當型別S是型別A的子型別,則Queue[S]不可認為是Queue[A]的子型別或父型別,這種情況是和Java一樣的。
2. 協變
trait Queue[+T] {}
這是協變情況。這種情況下,當型別S是型別A的子型別,則Queue[S]也可以認為是Queue[A}的子型別,即Queue[S]可以泛化為Queue[A]。也就是被引數化型別的泛化方向與引數型別的方向是一致的,所以稱為協變。
3. 逆變
trait Queue[-T] {}
這是逆變情況。這種情況下,當型別S是型別A的子型別,則Queue[A]反過來可以認為是Queue[S}的子型別。也就是被引數化型別的泛化方向與引數型別的方向是相反的,所以稱為逆變。
4. 型別下界
U >: T
這是型別下界的定義,也就是U必須是型別T的父類(或本身,自己也可以認為是自己的父類)。
5. 型別上屆
S <: T
這是型別上界的定義,也就是S必須是型別T的子類(或本身,自己也可以認為是自己的子類)。
泛型與約束實戰1 泛型函式
ClassTag[T]儲存了泛型擦除後的原始型別T,提供給被執行時的。
/*
* 泛型[],中括號F、S、T都表示執行時引數型別,
* ClassTag[T]儲存了泛型擦除後的原始型別T,提供給被執行時的。
*/
class Triple[F: ClassTag, S, T](val first: F, val second: S, val third: T)
object HelloTypeParam {
def main(args: Array[String]): Unit = {
// 執行執行程式碼:val triple: Triple[String, Int, Double]
val triple = new Triple("Spark", 3, 3.1415)
// 執行執行程式碼:val bigData: Triple[String, String, Char]
val bigData = new Triple[String, String, Char]("Spark", "Hadoop", 'R');
// getData函式傳入泛型為T的執行時List型別引數,返回list.length / 2的整數。
def getData[T](list:List[T]) = list(list.length / 2)
// List索引從0開始,執行結果:Hadoop
println(getData(List("Spark","Hadoop",'R')));
// 獲得getData函式引用
val f = getData[Int] _
// 呼叫getData函式,執行結果:4
println(f(List(1,2,3,4,5,6)));
}
}
2 型別變數界定
泛型引數型別限定,限定具體類的可以呼叫特定的方法。
/*
* <:泛型型別限定符,表示只限定Comparable子類
* Comparable[T]:為T下界,T:為Comparable[T]上界
*/
class Pair[T <: Comparable[T]](val first: T, val second: T) {
// compareTo方法進行比較,如果大於0返回first
def bigger = if (first.compareTo(second) > 0) first else second
}
// 宣告帶T泛型引數的類
class Pair_Lower_Bound[T](val first: T, val second: T) {
// 傳入的引數泛型T 必須為 R的父類(超類),返回構造Pair_Lower_Bound物件
// R:為T的上界,T:為R下界
def replaceFirst[R>:T](newFirst:R) = new Pair_Lower_Bound[R](newFirst,second)
}
object TypeVariablesBounds {
def main(args: Array[String]): Unit = {
// 函式呼叫
var pair = new Pair("Spark", "Hadoop")
// 執行結果:Spark
println(pair.bigger)
}
}
3 泛型檢視限定
泛型檢視限定:表示把傳入不是Comparable[T]型別的隱式傳換為Comparable[T]型別,Comparable[T]:為T下界,T:為Comparable[T]上界。
/*
* <%泛型檢視限定符,表示把傳入不是Comparable[T]型別的 隱式傳換 為Comparable[T]型別
* Comparable[T]:為T下界,T:為Comparable[T]上界
*/
class PairNotPerfect[T <% Comparable[T]](val first: T, val second: T) {
// compareTo方法進行比較,如果大於0返回first
def bigger = if (first.compareTo(second) > 0) first else second
}
/*
* <%泛型檢視限定符,表示把傳入不是Ordered[T]型別的 隱式傳換 為Ordered[T]型別
* Ordered[T]:為T下界,T:為Ordered[T]上界
* Ordered繼承: extends Any with java.lang.Comparable[A]
*/
class PairBetter[T <% Ordered[T]](val first: T, val second: T) {
def bigger = if (first.compareTo(second) > 0) first else second
}
object ViewVariablesBounds {
def main(args: Array[String]): Unit = {
// 函式呼叫
var pair = new PairNotPerfect("Spark", "Hadoop");
// 執行結果:Spark
println(pair.bigger)
// 函式呼叫,Int型別進行隱式轉換,將Int -> RichInt,RichInt實現了Comparable介面
var pairInt = new PairNotPerfect(3,5)
// 執行結果:5
println(pairInt.bigger);
// 函式呼叫,Int型別進行隱式轉換,將String -> RichString,RichString實現了Comparable介面
var pairBetterStr = new PairBetter("Java","Scala");
println(pairBetterStr.bigger);
// 函式呼叫
var pairBetterInt = new PairBetter(20, 12);
// 執行結果:Spark
println(pairBetterInt.bigger)
}
}
4 上下文界定
上下文界定:上下文界定是隱式引數的語法糖。如:Ordering:可以進行隱式轉化的T型別。
class PairOrdering[T: Ordering](val first: T, val second: T) {
// compareTo方法進行比較,如果大於0返回first
def bigger(implicit ordered: Ordering[T]) = if (ordered.compare(first, second) > 0) first else second
}
object ContextBounds {
def main(args: Array[String]): Unit = {
// 函式呼叫
var pair = new PairOrdering("Spark", "Hadoop")
// 執行結果:Spark
println(pair.bigger)
}
}
5 Manifest關鍵字
Manifest關鍵字:陣列在宣告時必須要求指定具體的型別,在函式泛型是無法知道具體型別,通過Manifest關鍵字使得執行時可以根據這個Manifest引數做更多的事情。
def main(args: Array[String]): Unit = {
/*
* 定義方法array
* Manifest:需要執行時儲存T的實際型別,執行時是做為引數執行在方法的上下文中。
* 陣列在定義時必須知道具體的型別,所以在宣告方法時,需要新增Manifest
*/
def arrayMake[T: Manifest](first: T, second: T) = {
val r = new Array[T](2);
r(0) = first;
r(1) = second;
// 返回r
r;
}
/*
* 執行結果:
* 1
* 2
*/
arrayMake(1,2).foreach(println)
}
6 ClassTag關鍵字
ClassTag[T]儲存了泛型擦除後的原始型別T,提供給被執行時的。
def main(args: Array[String]): Unit = {
/*
* ClassTag:在執行時指定,在編譯時無法確定的
*/
def mkArray[T:ClassTag](elems:T*) = Array[T](elems:_*)
/*
* 執行結果:
* 42
* 13
*/
mkArray(42,13).foreach(println)
/*
* 執行結果:
* Japan
* Brazil
* Germany
*/
mkArray("Japan","Brazil","Germany").foreach(println)
}
7 ClassManifest關鍵字
在引入Manifest的時候,還引入了一個更弱一點的ClassManifest,所謂的弱是指型別資訊不如Manifest那麼完整。用TypeTag替代了Manifest,用ClassTag替代了ClassManifest,原因是在路徑依賴型別中,Manifest存在問題。
class A[T]
val m = manifest[A[String]]
// 執行結果:com.scala.type_param.Manifest_ClassTag$A$1[java.lang.String]
println(m);
val cm = classManifest[A[String]]
// 執行結果:com.scala.type_param.Manifest_ClassTag$A$1[java.lang.String]
println(cm);
8 多重界定符
/*
// 表示: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
*/
9 Scala型別約束
def main(args: Array[String]): Unit = {
// A =:=B // 表示A型別等同於B型別
// A <:<B // 表示A型別是B型別的子類
def rocky[T](i: T)(implicit ev: T <:< java.io.Serializable) {
// 執行結果:Life is short ,you need spark!!!
println("Life is short ,you need spark!!!")
}
rocky("Spark")
}
更多scala,spark,大資料知識,希望擴充套件視野解決疑難雜症及困惑,歡迎點選閱讀原文,加入浪尖知識星球。
本文整理自網路,若有侵權,請聯絡微信158570986刪除。
推薦閱讀: