類型參數主要就是研究scala當中的類或者scala當中的方法的泛型
1、scala當中的類的泛型
object Demo8 { def main(args: Array[String]): Unit = { val result1 = new MyClass("hello",50) val result2 = new MyClass[Any,Any]("zhangsan","Lisi"); } }
/** * 定義一個class類,接收兩個參數,但是兩個參數都是泛型,泛型的類型,會根據我們 * 創建類的實例化對象的時候,動態的傳遞進行動態的推斷 * @param first * @param second * @tparam T * @tparam B */ class MyClass[T,B](first:T,second:B){ println(first+","+second)
} |
2、函數的泛型
我們的函數或者方法,也可以有類型參數
object methodType{ def getMiddle[T](canshu:T) ={ canshu } def main(args: Array[String]): Unit = { // 從參數類型來推斷類型 println(getMiddle(Array("Bob", "had", "a", "little", "brother")).getClass.getTypeName) //指定類型,並保存為具體的函數。 val f = getMiddle[String] _ println(f("Bob")) } |
3、scala當中的上下界之泛型類型的限定
在scala當中,我們可以通過上界或者下界來限定我們泛型的類型,類似於java當中的
? extends T ?號就表示我們使用的泛型,必須是T類型的子類,這種情況叫做上界
? super T ?號就表示我們使用的泛型,必須是T類型的父類,這種情況叫做下界
在scala當中上界的表示方法使用的是 "<:", 這個符號就是表示上界,這種形式稱之為泛型的上界。
在scala當中下界的表示方式使用的是 ">:", 這個符號就是表示下界,這種形式稱之為泛型的下界
3.1、泛型的上界限定
我們可以通過上界的限定,限定我們傳入的類型必須是某個類型的子類
class Pair1[T <: Comparable[T]](val first: T, val second: T) { def smaller = if (first.compareTo(second) < 0) first else second }
object Main1 extends App{ override def main(args: Array[String]): Unit = { val p = new Pair1("hello", "Brooks") println(p.smaller) } } |
3.2、泛型的下界限定
我們可以通過下界的限定,限定我們傳入的類型必須是某個類型的父類
class Pair2[T](val first: T, val second: T) { def replaceFirst[R >: T](newFirst: R) = new Pair2[R](newFirst, second) override def toString = "(" + first + "," + second + ")" }
object Main2 extends App{ override def main(args: Array[String]): Unit = { val p = new Pair2("Nick", "Alice") println(p) println(p.replaceFirst("Joke")) println(p) } } |
在Java中,T同時是A和B的子類型,稱之為多界,形式如:<T extends A & B>。
在Scala中,對上界和下界不能有多個,但是可以使用混合類型,如:[T <: A with B]。
在Java中,不支持下界的多界形式。如:<T super A & B>這是不支持的。
在Scala中,對復合類型依然可以使用下界,如:[T >: A with B]。
4、scala當中的視圖界定
說白了就是將我們的泛型轉化成了具體的類型
在Scala中,如果你想標記某一個泛型可以隱式的轉換為另一個泛型,可以使用:[T <% Comparable[T]],由於Scala的Int類型沒有實現Comparable接口,所以我們需要將Int類型隱式的轉換為RichInt類型,比如:
我們如果需要比較兩個值的大小,那么我們的兩個值必須是Comparable的子類,那么我們可以使用泛型 T <% Comparable 來限制我們泛型必須是Comparable的子類,並且我們的泛型在執行真正比較的方法的時候,會根據我們傳入的類型,自動推斷,進行隱式的轉換,例如我們傳入4,2 進行比較,那么我們會將4, 2 這兩個類型做自動推斷,轉換成真正的RichInt類型然后再繼續進行比較
/** * 使用 <% 來實現我們類型的隱式轉換 * @param first * @param second * @tparam T */ class Pair3[T <% Comparable[T]](val first: T, val second: T) { def smaller = if (first.compareTo(second) < 0) first else second override def toString = "(" + first + "," + second + ")" }
object Main3 extends App { val p = new Pair3(4, 2) println(p.smaller) } |
5、scala當中的協變,逆變和非變
協變和逆變主要是用來解決參數化類型的泛化問題。Scala的協變與逆變是非常有特色的,完全解決了Java中泛型的一大缺憾;舉例來說,Java中,如果有 A是 B的子類,但 Card[A] 卻不是 Card[B] 的子類;而 Scala 中,只要靈活使用協變與逆變,就可以解決此類 Java 泛型問題;
由於參數化類型的參數(參數類型)是可變的,當兩個參數化類型的參數是繼承關系(可泛化),那被參數化的類型是否也可以泛化呢?Java中這種情況下是不可泛化的,然而Scala提供了三個選擇,即協變("+")、逆變("-")和非變。
下面說一下三種情況的含義,首先假設有參數化特征Queue,那它可以有如下三種定義。
-
trait Queue[T] {}
這是非變情況。這種情況下,當類型B是類型A的子類型,則Queue[B]與Queue[A]沒有任何從屬關系,這種情況是和Java一樣的。
- trait Queue[+T] {}
這是協變情況。這種情況下,當類型B是類型A的子類型,則Queue[B]也可以認為是Queue[A]的子類型,即Queue[B]可以泛化為Queue[A]。也就是被參數化類型的泛化方向與參數類型的方向是一致的,所以稱為協變。 -
trait Queue[-T] {}
這是逆變情況。這種情況下,當類型B是類型A的子類型,則Queue[A]反過來可以認為是Queue[B]的子類型。也就是被參數化類型的泛化方向與參數類型的方向是相反的,所以稱為逆變。
協變、逆變、非變總結
- C[+T]:如果A是B的子類,那么C[A]是C[B]的子類。
- C[-T]:如果A是B的子類,那么C[B]是C[A]的子類。
- C[T]: 無論A和B是什么關系,C[A]和C[B]沒有從屬關系。
案例
package com.starzy.scala
class Super class Sub extends Super //協變 class Temp1[+A](title: String) //逆變 class Temp2[-A](title: String) //非變 class Temp3[A](title: String)
object Covariance_demo{ def main(args: Array[String]) { //支持協變 Temp1[Sub]還是Temp1[Super]的子類 val t1: Temp1[Super] = new Temp1[Sub]("hello scala!!!") //支持逆變 Temp1[Super]是Temp1[Sub]的子類 val t2: Temp2[Sub] = new Temp2[Super]("hello scala!!!") //支持非變 Temp3[Super]與Temp3[Sub]沒有從屬關系,如下代碼會報錯 //val t3: Temp3[Sub] = new Temp3[Super]("hello scala!!!") //val t4: Temp3[Super] = new Temp3[Sub]("hello scala!!!") println(t1.toString) println(t2.toString) } } |