kotlin泛型


/**
 * 泛型類
 */

class Animal<T> {}

/**
 * 泛型接口
 * @param T
 */
interface IAnimal<T> {
    fun <T> initAnimal(p: T)
}

//泛型約束
//泛型約束表示我們可以指定泛型類型(T)的上界,
//即父類型髓需,默認的上界為Any?,如果只有一個上界可以這樣指定
fun <T : Animal<T>> init(p: T) {}

//如果需要指定多個上界類型,就需要使用where語句:
fun <T> initA(p: T) where T : Animal<T>, T : IAnimal<T> {}

//類型擦除
//Kotlin 為泛型聲明執(zhí)行的類型安全檢測僅在編譯期進行淘太,
 運行時實例不保留關(guān)于泛型類型的任何信息行嗤。這一點在 Java 中也是類似的。
//
//例如砚偶,Array<String>挪钓、Array<Int>的實例都會被擦除為Array<*>是越,
//這樣帶來的好處是保存在內(nèi)存中的類型信息也就減少了。
//
//由于運行時泛型信息被擦除碌上,所以在運行時無法檢測一個
//實例是否是帶有某個類型參數(shù)的泛型類型倚评,
//所以下面的代碼是無法通過編譯的
(Cannot check for instance of erased type: Array<Int>):

//fun isArrays(a: Any) {
//  if (a is Array<Int>) {
//    println("is array")
//  }
//}
//但我們可以檢測一個實例是否是數(shù)組,
//雖然 Kotlin 不允許使用沒有指定類型參數(shù)的泛型類型馏予,
//但可以使用星投影*(這個后邊會說到):

fun isArray(a: Any) {
    if (a is Array<*>) {
        println("is array")
    }
}
//同樣原因天梧,由于類型被擦除,我們也無法安全
//的將一個實現(xiàn)轉(zhuǎn)換成帶有某個類型參數(shù)的泛型類型:

fun sumArray(a: Array<*>) {
    val intArray = a as? Array<Int> ?: throw IllegalArgumentException("Array的泛型類型必須是Int類型")
    println(intArray.sum())
}
//因為我們無法判斷數(shù)組a的是不是Array<Int>類型的霞丧,
//所以可能會出現(xiàn)異常的情況呢岗。
//
//對于泛型函數(shù),如果在函數(shù)內(nèi)需要使用具體的泛型類型蛹尝,
//同樣由于運行時泛型信息被擦除的原因后豫,
//你無法直接使用它(Cannot check for instance of erased type: T):

//fun < T> test(param: Any) {
//  if (param is T){
//    println("param type is match")
//  }
//}
//但還是有辦法的,可以用inline關(guān)鍵字修飾函數(shù)突那,
//即內(nèi)聯(lián)函數(shù)挫酿,這樣編譯器會把每一次函數(shù)調(diào)用都換成函數(shù)實際代碼實現(xiàn),
//同時用reified關(guān)鍵字修飾泛型類型陨收,這樣就能保留泛型參數(shù)的具體類型了:
inline fun <reified T> test(param: Any) {
    if (param is T) {
        println("param type is match")
    }
}
//型變
//1饭豹、聲明處型變
//型變是泛型中比較重要的概念,首先我們要知道 Kotlin 中
//的泛型是不型變的务漩,這點和 Java 類似。那什么是型變呢它褪,看個例子:

open class A
class B : A()


fun text() {
    val arrayB: Array<B> = arrayOf(B(), B())
    //val arrayA:Array<A> = arrayB
//    你會發(fā)現(xiàn)第二個賦值語句會有錯誤提示饵骨,
//Type mismatch. Required:Array<A> Found:Array<B>類型不匹配,
//    Array<B>并不是Array<A>的子類茫打,
就是因為 Kotlin 中的泛型是默認不型變的居触,
無法自動完成類型轉(zhuǎn)換,但B是A的子類老赤,
//    這個賦值操作本質(zhì)上是合理的轮洋、安全的,
//但編譯器似乎并不知道抬旺,這必然給我們開發(fā)過程中帶來了麻煩

//    為什么Array無法正常的賦值弊予,而List、Set开财、
//Map可以呢汉柒?如下代碼误褪,編譯器不會有錯誤提示的:
//
//    val list1: List<B> = listOf(B(), B(), B())
//    val list2: List<A> = list1
//    我們可以對比一下Array和List在源碼中的定義:
//
//    public class Array<T> {}
//
//    public interface List<out E> : Collection<E> {}

//    可以看到List的泛型類型使用了out修飾符,這就是關(guān)鍵所在了碾褂。
//這就是 Kotlin 中的聲明處型變兽间,用來向編譯器解釋這種情況。
//
//    關(guān)于out修飾符我們可這樣理解正塌,當(dāng)類嘀略、
//接口的泛型類型參數(shù)被聲明為out時,
//則該類型參數(shù)是協(xié)變的乓诽,泛型類型的子類型是被保留的屎鳍,
//    它只能出現(xiàn)在函數(shù)的輸出位置,只能作為返回類型问裕,
//即生產(chǎn)者逮壁。帶來的好處是,A是B的父類粮宛,
那么List<A>可以是List<B>的父類窥淆。

    // 我們修改下上邊List賦值的代碼:

    val list1: List<A> = listOf(A(), A(), A())
//    val list2: List<B> = list1
//    即反過來賦值,由于B并不是A的父類巍杈,
//會有Type mismatch. Required:List<B> Found:List<A>錯誤提示忧饭。
//    為了應(yīng)對這種情況,Kotlin 還提供了一個in修飾符筷畦。

//    關(guān)于in修飾符我們可這樣理解词裤,當(dāng)類、接口的泛型類型參數(shù)被聲明為in時鳖宾,
//則該類型參數(shù)是逆變的吼砂,泛型類型的父類型是被保留的,
//    它只能出現(xiàn)在函數(shù)的輸入位置鼎文,作為參數(shù)渔肩,只能作為消費類型,即消費者拇惋。
//    其實 Kotlin 中的Comparable接口使用了in修飾符:

//    寫一個測試函數(shù)周偎,編譯器并不會報錯:

    fun test(a: Comparable<A>) {
        val b: Comparable<B> = a
    }
   // 所以in修飾符和out修飾符的作用看起來的相對的,
//A是B的父類撑帖,那么Comparable<B>可以是Comparable<A>的父類蓉坎,體會下區(qū)別。

//    那么在 Kotlin 中我們自然想到的是型變修飾符了:
//
//    Kotlin 中的out A類似于 Java 中的? extends A胡嘿,
//即泛型參數(shù)類型必須是A或者A的子類蛉艾,用來確定類型的上限
//    Kotlin 中的in A類似于 Java 中的? super A,
//即泛型參數(shù)類型必須是B或者B的父類,用來確定類型的下限
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載伺通,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者箍土。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市罐监,隨后出現(xiàn)的幾起案子吴藻,更是在濱河造成了極大的恐慌,老刑警劉巖弓柱,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沟堡,死亡現(xiàn)場離奇詭異,居然都是意外死亡矢空,警方通過查閱死者的電腦和手機航罗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屁药,“玉大人粥血,你說我怎么就攤上這事∧鸺” “怎么了复亏?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缭嫡。 經(jīng)常有香客問我缔御,道長,這世上最難降的妖魔是什么妇蛀? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任耕突,我火速辦了婚禮,結(jié)果婚禮上评架,老公的妹妹穿的比我還像新娘眷茁。我一直安慰自己,他們只是感情好古程,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布蔼卡。 她就那樣靜靜地躺著,像睡著了一般挣磨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荤懂,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天茁裙,我揣著相機與錄音,去河邊找鬼节仿。 笑死晤锥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播矾瘾,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼女轿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了壕翩?” 一聲冷哼從身側(cè)響起蛉迹,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎放妈,沒想到半個月后北救,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡芜抒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年珍策,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宅倒。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡攘宙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拐迁,到底是詐尸還是另有隱情蹭劈,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布唠亚,位于F島的核電站链方,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏灶搜。R本人自食惡果不足惜祟蚀,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望割卖。 院中可真熱鬧前酿,春花似錦、人聲如沸鹏溯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丙挽。三九已至肺孵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颜阐,已是汗流浹背平窘。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凳怨,地道東北人瑰艘。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓是鬼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親紫新。 傳聞我的和親對象是個殘疾皇子均蜜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

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