Kotlin-15.泛型(generics)

官方文檔: http://kotlinlang.org/docs/reference/generics.html

1.泛型(generics)

與Java類似仿贬,Kotlin的類也有類型參數(shù)(泛型):        
    class Box<T>(t: T) {
        var value = t
    }
    
一般情況,使用泛型實例,需要類型參數(shù):
    val box: Box<Int> = Box<Int>(1)

如果類型參數(shù)可推斷出來,可省略類型參數(shù):
    val box = Box(1) // 1是Int,編譯器可推斷出Box<Int>

2.型變(Variance)

Java泛型中最棘手部分就是通配符類型(初學(xué)實在頭暈),但kotlin沒有!
所以Kotlin通過型變(Variance)彌補:
    聲明處型變(declaration-site variance)
    類型投影(type projections)

為什么Java泛型需要通配符類型超歌?
    在Effective Java解釋了該問題—第28條:利用有限制通配符來提升API的靈活性挟秤。 
    Java泛型是不型變的,意味著List<String>不是List<Object>子類型! 
    如果List是型變的,如下代碼編譯正常,但運行時出現(xiàn)異常:
        // Java
        List<String> strs = new ArrayList<String>();
        List<Object> objs = strs; //錯誤,Java禁止型變!
        objs.add(1);
        String s = strs.get(0); //運行出現(xiàn)異常ClassCastException:無法將整數(shù)轉(zhuǎn)為字符串瞻讽!

    因此,Java禁止型變以保證運行時的安全,但這樣會有一些影響,
    例如,假設(shè)Collection.addAll()參數(shù)如下:
        // Java
        interface Collection<E> …… {
            void addAll(Collection<E> items);
        } 
        void copyAll(Collection<Object> to, Collection<String> from) {
            //addAll不能編譯,Collection<String>不是Collection<Object>子類型
            to.addAll(from);      
        }
    這就是為什么Collection.addAll()實際參數(shù)如下:
        // Java
        interface Collection<E> …… {
            //通配符<? extends E>表示包括E在內(nèi)的所有子類,稱為協(xié)變(covariant)
            //通配符<? super E>表示包括E在內(nèi)的所有父類,稱為逆變(contravariance)
            void addAll(Collection<? extends E> items);
        }
        void copyAll(Collection<Object> to, Collection<String> from) {
            //<? extends E>可以讓Collection<String>是Collection<? extends Object>子類型
            to.addAll(from);
        }

    <? extends E>協(xié)變(covariant): 表示包括E在內(nèi)的所有子類,泛型對象只能讀取,稱為生產(chǎn)者
    <? super E>逆變(contravariance): 表示包括E在內(nèi)的所有父類,泛型對象只能寫入,稱為消費者
    助記符:Producer-extends, Consumer-super

3.聲明處型變(Declaration-site variance)

Java泛型的一個例子:
    interface Source<T> {
        //只有生產(chǎn)者方法,沒有消費者方法
        T nextT();
    }
    void demo(Source<String> strs) {
        //Source<T>沒有消費者方法,型變是安全的,但是Java并不知道,所以仍然禁止!
        //需要聲明類型為Source<? extends Object>,這是毫無意義的,更復(fù)雜類型并沒有帶來價值!            
        Source<Object> objects = strs; //錯誤:在Java中不允許型變
    }

1.out修飾符
在Kotlin中,可用out修飾類型參數(shù)T,確保T只能輸出(生產(chǎn)),不被消費!
out修飾符稱為型變注解(variance annotation),使類型參數(shù)協(xié)變(covariant)!    
    abstract class Source<out T> {
        abstract fun nextT(): T
    }
    fun demo(strs: Source<String>) {
        val objects: Source<Any> = strs //可以型變,因為T是out      
    }

2.in修飾符
用in修飾類型參數(shù)T,確保T只能被消費,不能輸出(生產(chǎn)),使類型參數(shù)逆變(contravariance)!
    abstract class Comparable<in T> {
        abstract fun compareTo(other: T): Int
    }
    fun demo(x: Comparable<Number>) {
        //1.0擁有Double類,是Number的子類
        x.compareTo(1.0)    

        //Double是Number的子類,父類Number可以被Double消費
        val y: Comparable<Double> = x
    }

助記符:消費者-輸入in, 生產(chǎn)者-輸出out
由于in/out在類型參數(shù)聲明處,所以稱為聲明處型變(Declaration-site variance)

4.使用處型變(Use-site variance)/類型投影(Type projections)

類型參數(shù)T既不是協(xié)變,也不是逆變(T既生產(chǎn)out,又消費in):
    class Array<T>(val size: Int) {
        fun get(index: Int): T {...} //生產(chǎn)out
        fun set(index: Int, value: T) {...} //消費in
    }     
    fun copy(from: Array<Any>, to: Array<Any>) {
        assert(from.size == to.size)
        for (i in from.indices)
            to[i] = from[i]
    }
    val ints: Array<Int> = arrayOf(1, 2, 3)
    val anys = Array<Any>(3) { "" } 
    copy(ints, anys) //錯誤:期望(Array<Any>, Array<Any>)

1.out,確保from中的Any只生產(chǎn)輸出,不被消費,對應(yīng)于Java的Array<? extends Object>:
    fun copy(from: Array<out Any>, to: Array<Any>) {
        ...
    }

2.in,確保dest中的String只能被消費,不生產(chǎn)輸出,對應(yīng)于Java的Array<? super String>
    fun fill(dest: Array<in String>, value: String) {
        ...
    }    

5.星投影<*>(Star-projections)

如果對類型參數(shù)一無所知,可用星投影:
    1.對于Foo<out T>,T是一個具有上界TUpper的協(xié)變類型參數(shù),Foo<*>等價于Foo<out TUpper>, 
    當(dāng)T未知時,可以安全地從Foo<*>讀取TUpper的值

    2.對于Foo<in T>,T是一個逆變類型參數(shù),Foo<*>等價于Foo<in Nothing>,
    當(dāng)T未知時,沒有什么方式可以安全寫入Foo<*>

    3.對于Foo<T>,T是一個具有上界TUpper的不型變類型參數(shù),
    Foo<*>在讀取值時等價于Foo<out TUpper>,在寫入值時等價于Foo<in Nothing>

如果有多個類型參數(shù),則每個類型參數(shù)都可單獨投影:
    interface Function <in T, out U>
    Function<*, String>   表示Function<in Nothing, String>
    Function<Int, *>      表示Function<Int, out Any?>
    Function<*, *>        表示Function<in Nothing, out Any?>
注意:星投影非常像Java的原始類型,但是安全!

6.泛型函數(shù)

和java類似, kotling不僅類有泛型,函數(shù)也有泛型:
    //普通函數(shù)
    fun <T> singletonList(item: T): List<T> {
        // ……
    }

    //擴展函數(shù)
    fun <T> T.basicToString() : String {  
        // ……
    }

    //調(diào)用泛型函數(shù),在函數(shù)名后指定類型參數(shù)
    val l = singletonList<Int>(1)

7.泛型約束

最常見的約束類型是,與Java的<? extends T>對應(yīng)的上界:
    //<T : Comparable<T>>冒號之后指定類型上界,只有Comparable<T>子類型可以替代T
    fun <T : Comparable<T>> sort(list: List<T>) {
    }
    sort(listOf(1, 2, 3)) //Int是Comparable<Int>子類型
    sort(listOf(HashMap<Int, String>())) //錯誤: HashMap<Int, String>不是Comparable<HashMap<Int, String>>子類型

默認上界是Any?,<:上界>中只能指定一個上界,如果同一類型參數(shù)需要多個上界,需要一個單獨的where子句:
    fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
        where T : Comparable, T : Cloneable {
        return list.filter { it > threshold }.map { it.clone() }
    }   

簡書:http://www.reibang.com/p/e9e743baa77a
CSDN博客: http://blog.csdn.net/qq_32115439/article/details/73656998
GitHub博客:http://lioil.win/2017/06/23/Kotlin-generics.html
Coding博客:http://c.lioil.win/2017/06/23/Kotlin-generics.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筷频,一起剝皮案震驚了整個濱河市鸠澈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌截驮,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件际度,死亡現(xiàn)場離奇詭異葵袭,居然都是意外死亡,警方通過查閱死者的電腦和手機乖菱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門坡锡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓬网,“玉大人,你說我怎么就攤上這事鹉勒》妫” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵禽额,是天一觀的道長锯厢。 經(jīng)常有香客問我,道長脯倒,這世上最難降的妖魔是什么实辑? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮藻丢,結(jié)果婚禮上剪撬,老公的妹妹穿的比我還像新娘。我一直安慰自己悠反,他們只是感情好残黑,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斋否,像睡著了一般梨水。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上如叼,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天冰木,我揣著相機與錄音,去河邊找鬼笼恰。 笑死踊沸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的社证。 我是一名探鬼主播逼龟,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼追葡!你這毒婦竟也來了腺律?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤宜肉,失蹤者是張志新(化名)和其女友劉穎匀钧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谬返,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡之斯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了遣铝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佑刷。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡莉擒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘫絮,到底是詐尸還是另有隱情涨冀,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布麦萤,位于F島的核電站鹿鳖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏频鉴。R本人自食惡果不足惜栓辜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垛孔。 院中可真熱鬧藕甩,春花似錦、人聲如沸周荐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽概作。三九已至腋妙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間讯榕,已是汗流浹背骤素。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愚屁,地道東北人济竹。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像霎槐,于是被迫代替她去往敵國和親送浊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

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