scala 系列之 18scala 泛型

22 泛型

泛型就是不確定的類型袜硫,可以在類或方法不確實傳入類型時使用箭阶,可以提高代碼的靈活性和復用性挤安;

scala中泛型的用法和java中差不多谚殊,但是會有一些自己獨特的語法;

泛型類:指定類可以接受任意類型參數(shù)蛤铜。

泛型方法:指定方法可以接受任意類型參數(shù)嫩絮。

22.1 泛型類基本用法

package day04
import day04.SexEnumObj.SexEnum
// 定義帶有泛型的抽象類
abstract class FXDemo[T](val t : T) {
  def printInfo():Unit
}
// 子類繼承父類,把泛型具體化成Int
class FXIntDemo[Int](t : Int) extends FXDemo[Int](t:Int){
  override def printInfo(): Unit = {
    println(s"FXIntDemo[Int](${t})")
  }
}
// 子類繼承父類围肥,把泛型具體化成String
class FXStringDemo[String](t : String) extends FXDemo[String](t:String){
  override def printInfo(): Unit = {
    println(s"FXIntDemo[String](${t})")
  }
}
// 定義帶有多泛型的類
class FXABCDemo[A, B, C](val a:A, val b:B, val c:C){
  override def toString: String = s"${a}, $剿干, ${c}"
}
// 定義sex的枚舉對象
object SexEnumObj extends Enumeration{
  // 定義枚舉類型(用于泛型)
  type SexEnum = Value
  // 定義枚舉值
  val boy, girl = Value
}
object FXDemo{
  def main(args: Array[String]): Unit = {
    val demo = new FXIntDemo[Int](100)
    demo.printInfo()
    val demo2 = new FXStringDemo[String]("xiaoming")
    demo2.printInfo()
    val demo3 = new FXABCDemo[String, Int, String]("xiaoming", 20, "boy")
    println(demo3)
    val demo4 = new FXABCDemo[String, Int, SexEnum]("xiaoming", 20, SexEnumObj.boy)
    println(demo4)
  }
}

22.2 泛型種類

[B <: A] UpperBound 上界,B類型的父類是A類型穆刻,左側(cè)的B的類型必須是A類型或者A類型的子類怨愤。

[B >: A] LowerBound 下界,B類型的子類是A類型蛹批,左側(cè)的B的類型必須是A類型或者A類型的父類撰洗。

[B <% A] ViewBound B類型轉(zhuǎn)換成A類型篮愉,需要一個隱式轉(zhuǎn)換函數(shù)。

[B : A] ContextBound 通過隱式轉(zhuǎn)換增加個 A[B] 類型差导。

[-A] 逆變试躏,通常作為參數(shù)類型,T是A的子類设褐。

[+B] 協(xié)變颠蕴,通常作為返回類型,T是B的父類助析。

22.2.1 UpperBound

UpperBound 用在泛型類或泛型方法上犀被,用于限制傳遞的參數(shù)必須是 指定類型對象或其子類對象。

如果想實現(xiàn)兩個對象的比較外冀,需要該對象實現(xiàn)Comparable接口寡键。然后再配上泛型實現(xiàn)通用比較。

泛型繼承雪隧,java的用法

package javaday04;
public class UpperBoundDemo<T extends Comparable<T>> {
    // psvm
    public static void main(String[] args) {
        // Integer 實現(xiàn)了 Comparable接口西轩,創(chuàng)建對象時,約束通過
        UpperBoundDemo<Integer> demo1 = new UpperBoundDemo<Integer>();
        // Hainiu 沒實現(xiàn) Comparable接口脑沿,創(chuàng)建對象時藕畔,約束不通過
//        UpperBoundDemo<Hainiu> demo2 = new UpperBoundDemo<Hainiu>();
        // 約束通過
        UpperBoundDemo<HainiuComparable> demo3 = new UpperBoundDemo<HainiuComparable>();
    }
}
class Hainiu{}
class HainiuComparable implements Comparable<HainiuComparable>{
    public int compareTo(HainiuComparable o) {
        return 0;
    }
}

泛型繼承,scala用法

package day04
// 在類上定義泛型庄拇, new對象時就會約束
class UpperBoundDemo[T <: Comparable[T]](val t:T) {
}

object UpperBoundDemo{
  def main(args: Array[String]): Unit = {
    // 因為 Integer 是 Comparable 實現(xiàn)類注服,所以約束通過
    val demo = new UpperBoundDemo[Integer](100)
    println(demo.t)
   
    // 約束通過
    val demo2 = new UpperBoundDemo[HainiuComparable](new HainiuComparable())
  }
}

class HainiuComparable implements Comparable<HainiuComparable>{
    public int compareTo(HainiuComparable o) {
        return 0;
    }
}

22.2.2 LowerBound

LowerBound 用在泛型類或泛型方法上,用于限制傳遞的參數(shù)必須是 指定類型對象或其父類對象措近。

package day04
class LowerBoundDemo[T] {
  // 將lowerbound約束到方法上
  def say[R>:T](r:R) = println(s"say ${r}")
}
object LowerBoundDemo{
  def main(args: Hainiurray[String]): Unit = {
    // new對象時指定泛型是Hainiu類型
    val demo = new LowerBoundDemo[Hainiu]
    // say方法是lowerbound約束溶弟, 只有Hainiu或Hainiu的父類可以通過約束
    demo.say[Hainiu](new Hainiu)
    demo.say[HainiuSupper](new HainiuSupper)
    // Hainiu的子類是不能通過約束的
//    demo.say[HainiuSub](new HainiuSub)
    // 如果調(diào)用方法時不加泛型,那就約束通過(不約束)
    demo.say(new HainiuSub)
  }
}
class HainiuSupper
class Hainiu extends HainiuSupper
class HainiuSub extends Hainiu

22.2.3 ViewBound [B <% A]

ViewBound 用在泛型類或泛型方法上熄诡,通過隱式轉(zhuǎn)換把 B類型轉(zhuǎn)換成A類型可很。

示例:

已知類 HainiuWork,對HainiuWork的兩個對象作比較凰浮。

class HainiuWork(val company: String, val money: Int, val holiday: Int) {
  override def toString: String = {
    s"company:${company}, money:${money}, holiday:${holiday}"
  }
}

java的實現(xiàn)方法:定義外部比較器我抠,用外部比較器去實現(xiàn)。

scala 用 viewbound 實現(xiàn)方法:通過隱式轉(zhuǎn)換 將 HainiuWork 轉(zhuǎn)成 Ordered[HainiuWork]袜茧,然后通過Ordered 實現(xiàn)比較菜拓。

Ordered 是什么?

Ordered特質(zhì)更像是rich版的Comparable接口笛厦,除了compare方法外纳鼎,更豐富了比較操作(<, >, <=, >=)。

trait Ordered[A] extends Any with java.lang.Comparable[A] {
    def compare(that: A): Int    // 內(nèi)部比較器的比較方法
    def <  (that: A): Boolean = (this compare that) <  0
    def >  (that: A): Boolean = (this compare that) >  0
    def <= (that: A): Boolean = (this compare that) <= 0
    def >= (that: A): Boolean = (this compare that) >= 0
    def compareTo(that: A): Int = compare(that)   // 外部比較器的比較方法

比較代碼實現(xiàn):

// Ordered 支持 > 操作
class ViewBoundDemo[W <% Ordered[W]] {
  def choose(work1: W, work2: W): W = {
    if (work1 > work2) work1 else work2
  }
}
object ViewBoundDemo {
  def main(args: Array[String]): Unit = {
    import MyPredef.selectWork
    val demo = new ViewBoundDemo[HainiuWork]
    
    val work1 = new HainiuWork("金融", 20000, 16)
    val work2 = new HainiuWork("互聯(lián)網(wǎng)", 20000, -6)
        
    // 通過隱式轉(zhuǎn)換將 HainiuWork --> Ordered[HainiuWork]
    println(demo.choose(work1, work2))
  }
}
class HainiuWork(val company: String, val money: Int, val holiday: Int) {
  override def toString: String = {
    s"go to ${company},happy holiday ${holiday}"
  }
}

隱式轉(zhuǎn)換函數(shù)實現(xiàn)

  implicit val selectWork = (s:HainiuWork) => new Ordered[HainiuWork]{
    override def compare(that: HainiuWork) = {
      // 先比較工資,工資相同比較假期
      if(s.money == that.money){
        s.holiday - that.holiday
      }else{
        s.money - that.money
      }
    }
  }

22.2.4 ContextBound [B : A]

ContextBound 用在泛型類或泛型方法上贱鄙,通過隱式轉(zhuǎn)換給B類型增加個 隱式值A[B]類型劝贸。

還是對HainiuWork的兩個對象作比較。

scala 用 ContextBound 實現(xiàn)方法: 通過隱式轉(zhuǎn)換 將 HainiuWork 轉(zhuǎn)成 Ordering[HainiuWork] 逗宁。

file

實現(xiàn)邏輯:

package day04
class ContextBoundDemo[T] {
  // 帶有隱式參數(shù)的方法映九,那調(diào)用時,當前環(huán)境得有Ordering[T] 類型的隱式對象
  def chooseBigger(w1:T, w2:T)(implicit ord:Ordering[T]):T = {
    if(ord.gt(w1, w2)) w1 else w2
  }
}
object ContextBoundDemo{
  def main(args: Array[String]): Unit = {
    val w1 = new HainiuWork("互聯(lián)網(wǎng)1", 30000, -3)
    val w2 = new HainiuWork("互聯(lián)網(wǎng)2", 20000, 20)
    val demo = new ContextBoundDemo[HainiuWork]
    // 在調(diào)用前瞎颗,引入隱式對象
    import util.MyPredef.HWOrdering
    val bigger = demo.chooseBigger(w1, w2)
    println(s"更幸福的是:${bigger}")
  }
}

升級版:

package day04
// 當new對象時件甥,通過隱式轉(zhuǎn)換會生成隱式的Ordering[T]
class ContextBoundDemo2[T : Ordering] {
  // 帶有隱式參數(shù)的方法,那調(diào)用時哼拔,當前環(huán)境得有Ordering[T] 類型的隱式對象
  def chooseBigger(w1:T, w2:T):T = {
    // 方法1:
//    val ord = implicitly[Ordering[T]]
    // 方法2:
//    val ord = Ordering[T]
//    if(ord.gt(w1, w2)) w1 else w2
    // 方法3:
    import Ordered.orderingToOrdered
    if(w1 > w2) w1 else w2
  }
}
object ContextBoundDemo2{
  def main(args: Array[String]): Unit = {
    val w1 = new HainiuWork("互聯(lián)網(wǎng)1", 30000, -3)
    val w2 = new HainiuWork("互聯(lián)網(wǎng)2", 20000, 20)
    import util.MyPredef.HWOrdering
    val demo = new ContextBoundDemo2[HainiuWork]
    val bigger = demo.chooseBigger(w1, w2)
    println(s"更幸福的是:${bigger}")
  }
}

22.2.5 協(xié)變與逆變

在聲明Scala的泛型類型時引有,“+”表示協(xié)變(covariance),而“-”表示逆變(contravariance)倦逐。

C[+T]:如果A是B的子類譬正,那么C[A]是C[B]的子類;通常作為返回值類型僻孝。

C[-T]:如果A是B的子類导帝,那么C[B]是C[A]的子類守谓;通常作為參數(shù)類型穿铆。

C[T]:無論A和B是什么關系,C[A]和C[B]沒有從屬關系斋荞。

file

一切圍繞著子類轉(zhuǎn)父類可以直接轉(zhuǎn)荞雏,但父類不能直接轉(zhuǎn)子類,也就是泛型約束平酿。

scala> class SuperA { def msuper() = println("SuperA") }
defined class SuperA
scala> class A extends SuperA { def m() = println("A") }
defined class A
scala> class SubA extends A { def msub() = println("SubA") }
defined class SubA
scala>
// 定義函數(shù) 入?yún)⑹?A類型凤优, 返回值是 A類型
scala> var func:(A)=>A = (a:A)=>a
func: A => A = <function1>
// 測試逆變
// function1 入?yún)⑹悄孀儯?返回值是協(xié)變
// SuperA > A > SubA   ---> 逆變[SuperA] < 逆變[A] < 逆變[SubA]
// 函數(shù)的入?yún)⑹荢uperA,
scala> func = (a:SuperA)=> new A
// 調(diào)用時蜈彼,將A對象傳入筑辨,可以調(diào)用A的方法,SuperA的方法 
scala> func(new A).m
A
scala> func(new A).msuper
SuperA
func: A => A = <function1>
// 因為  逆變[A] < 逆變[SubA]幸逆, 父類不能直接轉(zhuǎn)子類棍辕, 約束不通過
scala> func = (a:SubA)=> new A
<console>:15: error: type mismatch;
 found   : SubA => A
 required: A => A
       func = (a:SubA)=> new A
                      ^
// 測試協(xié)變
// SuperA > A > SubA   ---> 協(xié)變[SuperA] > 協(xié)變[A] > 協(xié)變[SubA]
scala> func = (a:A) => new A
func: A => A = <function1>
scala> func = (a:A) => new SubA
func: A => A = <function1>
// 因為  協(xié)變[A] < 協(xié)變[SuperA], 父類不能直接轉(zhuǎn)子類还绘, 約束不通過
scala> func = (a:A) => new SuperA
<console>:14: error: type mismatch;
 found   : SuperA
 required: A
       func = (a:A) => new SuperA

示例:

// 協(xié)變
class T1[+T](e:T)
val v1:T1[java.lang.Integer] = new T1(100)
val v2:T1[java.lang.Integer] = v1
// 因為 Integer extends Number楚昭,所以 T1[Integer] 可以轉(zhuǎn)換成 T1[Number], 這就是協(xié)變
val v3:T1[java.lang.Number] = v1 // 合法
// 反之不可以
val v4:T1[java.lang.Integer] = v3 //非法
// 逆變
class T2[-T](e:T)
val v1:T2[java.lang.Number] = new T2(100)
val v2:T2[java.lang.Number] = v1
// 因為Integer extends Number拍顷,所以 T1[Number] 可以轉(zhuǎn)換成T1[Integer]抚太,這就是逆變
val v3:T2[java.lang.Integer] = v1 // 合法
// 反之不可以
val v4:T2[java.lang.Number] = v3 // 非法
     

海汼部落原創(chuàng)文章,原文鏈接:http://hainiubl.com/topics/75753

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市尿贫,隨后出現(xiàn)的幾起案子电媳,更是在濱河造成了極大的恐慌,老刑警劉巖庆亡,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匆背,死亡現(xiàn)場離奇詭異,居然都是意外死亡身冀,警方通過查閱死者的電腦和手機钝尸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搂根,“玉大人珍促,你說我怎么就攤上這事∈@ⅲ” “怎么了猪叙?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仁卷。 經(jīng)常有香客問我穴翩,道長,這世上最難降的妖魔是什么锦积? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任芒帕,我火速辦了婚禮,結果婚禮上丰介,老公的妹妹穿的比我還像新娘背蟆。我一直安慰自己,他們只是感情好哮幢,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布带膀。 她就那樣靜靜地躺著,像睡著了一般橙垢。 火紅的嫁衣襯著肌膚如雪垛叨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天柜某,我揣著相機與錄音嗽元,去河邊找鬼。 笑死莺琳,一個胖子當著我的面吹牛还棱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惭等,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼珍手,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起琳要,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤寡具,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后稚补,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體童叠,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年课幕,在試婚紗的時候發(fā)現(xiàn)自己被綠了厦坛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡乍惊,死狀恐怖杜秸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情润绎,我是刑警寧澤撬碟,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站莉撇,受9級特大地震影響呢蛤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棍郎,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一其障、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坝撑,春花似錦静秆、人聲如沸粮揉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扶认。三九已至侨拦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辐宾,已是汗流浹背狱从。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叠纹,地道東北人季研。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像誉察,于是被迫代替她去往敵國和親与涡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容