首頁 > 軟體

Java Scala泛型(泛型方法,泛型類,泛型特質,上下界,協變、逆變、非變)

2023-04-04 06:01:18

1. 泛型

泛型的意思是泛指某種具體的資料型別, 在Scala中, 泛型用[資料型別]表示. 在實際開發中, 泛型一般是結合陣列或者集合來使用的, 除此之外, 泛型的常見用法還有以下三種:

  • 泛型方法
  • 泛型類
  • 泛型特質

1.1 泛型方法

泛型方法指的是把泛型定義到方法宣告上, 即:該方法的引數型別是由泛型來決定的. 在呼叫方法時, 明確具體的資料型別.

格式

def 方法名[泛型名稱](..) = {
    //...
}

需求

定義方法getMiddleElement(), 用來獲取任意型別陣列的中間元素.

  • 思路一: 不考慮泛型直接實現(基於Array[Int]實現)
  • 思路二: 加入泛型支援.

參考程式碼

//案例: 泛型方法演示.
//細節: 泛型方法在呼叫方法的時候 明確具體的資料型別.
object ClassDemo01 {
  //需求: 用一個方法來獲取任意型別陣列的中間的元素
  //思路一:不考慮泛型直接實現(基於Array[Int]實現)
  //def getMiddleElement(arr: Array[Int]) = arr(arr.length / 2)

  //思路二: 加入泛型支援
  def getMiddleElement[T](arr: Array[T]) = arr(arr.length / 2)

  def main(args: Array[String]): Unit = {
      //呼叫方法
      println(getMiddleElement(Array(1, 2, 3, 4, 5)))

      println(getMiddleElement(Array("a", "b", "c")))
  }
}

1.2 泛型類

泛型類指的是把泛型定義到類的宣告上, 即:該類中的成員的引數型別是由泛型來決定的. 在建立物件時, 明確具體的資料型別.

格式

class 類[T](val 變數名: T)

需求

  • 定義一個Pair泛型類, 該類包含兩個欄位,且兩個欄位的型別不固定.
  • 建立不同型別的Pair泛型類物件,並列印.

參考程式碼

//案例: 泛型-演示泛型類的使用.
//泛型類: 在建立物件的時候, 明確具體的資料型別.
object ClassDemo02 {
  //1. 實現一個Pair泛型類
  //2. Pair類包含兩個欄位,而且兩個欄位的型別不固定
  class Pair[T](var a:T, var b:T)

  def main(args: Array[String]): Unit = {
    //3. 建立不同型別泛型類物件,並列印
    var p1 = new Pair[Int](10, 20)
    println(p1.a, p1.b)

    var p2 = new Pair[String]("abc", "bcd")
    println(p2.a, p2.b)
  }
}

1.3 泛型特質

泛型特質指的是把泛型定義到特質的宣告上, 即:該特質中的成員的引數型別是由泛型來決定的. 在定義泛型特質的子類或者子單例物件時, 明確具體的資料型別.

格式

trait 特質A[T] {
  //特質中的成員
}

class 類B extends 特質A[指定具體的資料型別] {
  //類中的成員
}

需求

  • 定義泛型特質Logger, 該類有一個變數a和show()方法, 它們都是用Logger特質的泛型.
  • 定義單例物件ConsoleLogger, 繼承Logger特質.
  • 列印單例物件ConsoleLogger中的成員.

參考程式碼

//案例: 演示泛型特質.
object ClassDemo03 {
  //1. 定義泛型特質Logger, 該類有一個a變數和show()方法, 都是用Logger特質的泛型.
  trait Logger[T] {
    //定義變數
    val a:T

    //定義方法.
    def show(b:T) = println(b)
  }

  //2. 定義單例物件ConsoleLogger, 繼承Logger特質.
  object ConsoleLogger extends Logger[String]{
    override val a: String = "張三"
  }

  //main方法, 作為程式的主入口.
  def main(args: Array[String]): Unit = {
    //3. 列印單例物件ConsoleLogger中的成員.
    println(ConsoleLogger.a)
    ConsoleLogger.show("10")
  }
}

2. 上下界

在使用泛型(方法, 類, 特質)時,如果要限定該泛型必須從哪個類繼承、或者必須是哪個類的父類別。此時,就需要使用到泛型的上下界

2.1 上界

使用T <: 型別名表示給型別新增一個上界,表示泛型引數必須要從該類(或本身)繼承.

格式

[T <: 型別]

例如: [T <: Person]的意思是, 泛型T的資料型別必須是Person型別或者Person的子型別

需求

  • 定義一個Person類
  • 定義一個Student類,繼承Person類
  • 定義一個泛型方法demo(),該方法接收一個Array引數.
  • 限定demo方法的Array元素型別只能是Person或者Person的子類
  • 測試呼叫demo()方法,傳入不同元素型別的Array

參考程式碼

//案例: 演示泛型的上下界之  上界.
object ClassDemo04 {
  //1. 定義一個Person類
  class Person

  //2. 定義一個Student類,繼承Person類
  class Student extends Person

  //3. 定義一個demo泛型方法,該方法接收一個Array引數,
  //限定demo方法的Array元素型別只能是Person或者Person的子類
  def demo[T <: Person](arr: Array[T]) = println(arr)

  def main(args: Array[String]): Unit = {
    //4. 測試呼叫demo,傳入不同元素型別的Array
    //demo(Array(1, 2, 3))          //這個會報錯, 因為只能傳入Person或者它的子型別.

    demo(Array(new Person()))
    demo(Array(new Student()))
  }
}

2.2 下界

使用T >: 資料型別表示給型別新增一個下界,表示泛型引數必須是從該型別本身或該型別的父類別型.

格式

[T >: 型別]

注意:

例如: [T >: Person]的意思是, 泛型T的資料型別必須是Person型別或者Person的父類別型如果泛型既有上界、又有下界。下界寫在前面,上界寫在後面. 即: [T >: 型別1 <: 型別2]

需求

  • 定義一個Person類
  • 定義一個Policeman類,繼承Person類
  • 定義一個Superman類,繼承Policeman類
  • 定義一個demo泛型方法,該方法接收一個Array引數,
  • 限定demo方法的Array元素型別只能是Person、Policeman
  • 測試呼叫demo,傳入不同元素型別的Array

參考程式碼

//案例: 演示泛型的上下界之 下界.
//如果你在設定泛型的時候, 涉及到既有上界, 又有下界, 一定是: 下界在前, 上界在後.
object ClassDemo05 {
  //1. 定義一個Person類
  class Person
  //2. 定義一個Policeman類,繼承Person類
  class Policeman extends Person
  //3. 定義一個Superman類,繼承Policeman類
  class Superman extends Policeman

  //4. 定義一個demo泛型方法,該方法接收一個Array引數,
  //限定demo方法的Array元素型別只能是Person、Policeman
  //          下界          上界
  def demo[T >: Policeman <: Policeman](arr: Array[T]) = println(arr)

  def main(args: Array[String]): Unit = {
    //5. 測試呼叫demo,傳入不同元素型別的Array
    //demo(Array(new Person))
    demo(Array(new Policeman))
    //demo(Array(new Superman))     //會報錯, 因為只能傳入: Policeman類獲取它的父類別型, 而Superman是Policeman的子型別, 所以不行.
  }
}

3. 協變、逆變、非變

在Spark的原始碼中大量使用到了協變、逆變、非變,學習該知識點對閱讀spark原始碼很有幫助。

  • 非變: 類A和類B之間是父子類關係, 但是Pair[A]和Pair[B]之間沒有任何關係.
  • 協變: 類A和類B之間是父子類關係, Pair[A]和Pair[B]之間也有父子類關係.
  • 逆變: 類A和類B之間是父子類關係, 但是Pair[A]和Pair[B]之間是子父類別關係.

如下圖:

3.1 非變

語法格式

class Pair[T]{}
  • 預設泛型類是非變的
  • 即: 型別B是A的子型別,Pair[A]和Pair[B]沒有任何從屬關係 3.2 協變

語法格式

class Pair[+T]
  • 型別B是A的子型別,Pair[B]可以認為是Pair[A]的子型別
  • 引數化型別的方向和型別的方向是一致的。

3.3 逆變

語法格式

class Pair[-T]
  • 型別B是A的子型別,Pair[A]反過來可以認為是Pair[B]的子型別
  • 引數化型別的方向和型別的方向是相反的

3.4 範例

需求

  • 定義一個Super類、以及一個Sub類繼承自Super類
  • 使用協變、逆變、非變分別定義三個泛型類
  • 分別建立泛型類物件來演示協變、逆變、非變

參考程式碼

//案例: 演示非變, 協變, 逆變.
object ClassDemo06 {
  //1. 定義一個Super類、以及一個Sub類繼承自Super類
  class Super               //父類別
  class Sub extends Super   //子類

  //2. 使用協變、逆變、非變分別定義三個泛型類
  class Temp1[T]            //非變
  class Temp2[+T]           //協變
  class Temp3[-T]           //逆變.

  def main(args: Array[String]): Unit = {
    //3. 分別建立泛型類來演示協變、逆變、非變
    //演示非變.
    val t1:Temp1[Sub] = new Temp1[Sub]
    //val t2:Temp1[Super] = t1          //編譯報錯, 因為非變是: Super和Sub有父子類關係, 但是Temp1[Super] 和 Temp1[Sub]之間沒有關係.

    //演示協變
    val t3:Temp2[Sub] = new Temp2[Sub]
    val t4:Temp2[Super] = t3          //不報錯, 因為協變是: Super和Sub有父子類關係, 所以Temp2[Super] 和 Temp2[Sub]之間也有父子關係.
                                      //Temp2[Super]是父類別型,   Temp2[Sub]是子型別.

    //演示逆變
    val t5:Temp3[Super]  = new Temp3[Super]
    val t6:Temp3[Sub] = t5          //不報錯, 因為逆變是:  Super和Sub有父子類關係, 所以Temp3[Super] 和 Temp3[Sub]之間也有子父關係.
                                    //Temp3[Super]是子型別,   Temp3[Sub]是父類別型.
  }
}

到此這篇關於Scala泛型(泛型方法,泛型類,泛型特質,上下界,協變、逆變、非變)的文章就介紹到這了,更多相關Scala泛型內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com