Scala 泛型以及泛型約束

泛型類

  • 在類聲明時(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]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市翅雏,隨后出現(xiàn)的幾起案子圈驼,更是在濱河造成了極大的恐慌,老刑警劉巖望几,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绩脆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)靴迫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門惕味,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人玉锌,你說(shuō)我怎么就攤上這事名挥。” “怎么了芬沉?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵躺同,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我丸逸,道長(zhǎng)蹋艺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任黄刚,我火速辦了婚禮捎谨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘憔维。我一直安慰自己涛救,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布业扒。 她就那樣靜靜地躺著检吆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪程储。 梳的紋絲不亂的頭發(fā)上蹭沛,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音章鲤,去河邊找鬼摊灭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛败徊,可吹牛的內(nèi)容都是我干的帚呼。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼皱蹦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼煤杀!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起沪哺,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怜珍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后凤粗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酥泛,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡今豆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柔袁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呆躲。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捶索,靈堂內(nèi)的尸體忽然破棺而出插掂,到底是詐尸還是另有隱情,我是刑警寧澤腥例,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布辅甥,位于F島的核電站,受9級(jí)特大地震影響燎竖,放射性物質(zhì)發(fā)生泄漏璃弄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一构回、第九天 我趴在偏房一處隱蔽的房頂上張望夏块。 院中可真熱鬧,春花似錦纤掸、人聲如沸脐供。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)政己。三九已至,卻和暖如春掏愁,著一層夾襖步出監(jiān)牢的瞬間歇由,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工托猩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辽慕。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓京腥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親溅蛉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子公浪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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