泛型

1.kotlin和java一样支持类型参数:

class Box<T>(t: T) {
    var value = t
}

你可以像下面这样创建实例,你需要提供类型参数:

val box: Box<Int> = Box<Int>(1)

假如类型参数是可以推断的,比如根据构造函数的参数等等,类型参数是可以忽略的,如下所示:

val box = Box(1) // 1 has type Int, so the compiler figures out that we are talking about Box<Int>

2.型变

Java 类型系统中最棘手的部分之一是通配符类型,如果不了解java通配符的同学可以先了解 java通配符的使用,而在kotlin中没有通配符,为了解决java中遇到的问题,提出了另外两个东西:声明处型变(declaration-site variance)与类型投影(type projections)

3.声明处型变

声明处形变outin
out协变的类型参数示例:

open class Animal {
}
open class Suckler: Animal() {
}
class Dog: Suckler() {
}
class Source<out T> {
    fun get():T? {
        return null
    }

    //out 声明的类型参数不能作为参数传入,否则编译不通过
//    fun add(t: T) {
//
//    }

    //只能将Suckler或其子类赋值给t
    //好比我有一个只能往外取Suckler的箱子,你把这个箱子换成装Dog的箱子也是没问题的,因为你取出来的Dog可以当Suckler使用
    fun match(source: Source<Dog>) {
        var t: Source<Suckler> = source
//        val dog: Dog = t.get()
    }

    //out声明的泛型只能将String或者子类赋值给t,下面编译不通过
//    fun mismatch(tree: Source<Animal>) {
//        var t: Source<Dog> = tree
//    }
}
  • out修饰的类型参数是生产者如Source<out T>,只允许从source中返回T(即生产),而不能添加(作为参数传入)
  • 可以将子类赋值给父类
  • Array<out Any>相当于java的Array<? extends Object>

in:逆变的类型参数示例:

class Sink<in T> {
    fun add(t: T) {

    }

    //in修饰的类型参数不能返回,否则会编译不通过
//    fun get() : T? = null

    //只能将Suckler类型或其父类赋值给 t
    //好比我有一个只能往里面放Suckler的箱子,你把它换成可以装Animal的箱子也是没问题的,因为Suckler和Dog都可以往里面放
    fun match(sink: Sink<Animal>) {
        var t: Sink<Suckler> = sink
        t.add(Suckler())
        t.add(Dog())
//        t.add(Animal)
    }

    //编译不通过,报Type mismatch
//    fun dismiss(router: Sink<Suckler>) {
//        var t: Sink<Animal> = router
//    }
}
  • 和out相反in修饰的参数类型只能被消费(可以作为参数传入)而不可以被生产(返回)
  • 可以将父类赋值给子类
  • Array<in String> 相当于java的 Array<? super String>

4.使用处型变

考虑下面的问题:

class Array<T>(val size: Int) {
    fun get(index: Int): T { ///* …… */ }
    fun set(index: Int, value: T) { ///* …… */ }
}
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 any = Array<Any>(3) { "" } 
copy(ints, any) // 错误:期望 (Array<Any>, Array<Any>)

我们可以在使用初形变解决这个问题:

fun copy(from: Array<out Any>, to: Array<Any>) {
 // ......
}

类型投影:这里的 from 不仅仅是一个数组,而是一个受限制的(投影 的)数组。
或者使用 in 做类型投影:

fun fill(dest: Array<in String>, value: String) {
    // ......
} 

Array<in String> 对应于 Java 的 Array<? super String> ,也就是说,你可以传递一个 CharSequence 数组或一个 Object 数组给 fill() 函数。

5. 星投影

class Bar<in T, out U>() {
    fun add(t: T) {

    }

    fun get(): U? {
        return null
    }
}

fun test0(bar: Bar<*, Suckler>) {
    //添加不了,因为我不知道你的类型是什么
//    bar.add(Animal())
}

fun test1(bar: Bar<Suckler, *>) {
    bar.add(Suckler())
    bar.add(Dog())

    bar.get()
}

fun test2(bar: Bar<*, *>) {
    //添加不了,因为我不知道你的类型是什么
//    bar.add(Animal())
    
    bar.get()
}

你对类型参数一无所知,但仍然希望以安全的方式使用它。 这里的安全方式是 定义泛型类型的这种投影,该泛型类型的每个具体实例化将是该投影的子类型。
星投影语法:
假设类型被声明 为 interface Function <in T, out U> ,我们可以想象以下星投影:

  • Function<*, String> 表示 Function<in Nothing, String> ;
  • Function<Int, *> 表示 Function<Int, out Any?> ;
  • Function<*, *> 表示 Function<in Nothing, out Any?>

6.泛型函数

fun <T> singletonList(item: T): List<T> {
    // ......
}
fun <T> T.basicToString() : String { // 扩展函数 
        // ......
}

val l = singletonList<Int>(1)

7.泛型约束

fun <T : Comparable<T>> sort(list: List<T>) {
    // ......
}

冒号之后指定的类型是上界:只有Comparable<T> 的子类型可以替代 T 。

8.具体化的类型参数

内联函数支持具体化的类型参数。下面看看我们不使用具体化的类型参数的情况:

fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
    var p = parent
    while (p != null && !clazz.isInstance(p)) {
        p = p.parent
    }
    @Suppress("UNCHECKED_CAST")
    return p as T?
}

 treeNode.findParentOfType(MyTreeNode::class.java)

上面的代码使用了扩展函数的语法,如对扩展函数不熟悉,请查看扩展函数的语法。
让我们看看下面使用具体化的类型参数的例子:

inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p.parent
    }
    return p as T?
}

myTree.findParentOfType<MyTreeNodeType>()

我们使用 reified 修饰符来限定类型参数,现在可以在函数内部访问它了, 几乎就像是一个 普通的类一样。由于函数是内联的,不需要反射,正常的操作符如 !is 和 as 现在都能用了。如对内联函数不熟悉,请查看内联函数的语法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,215评论 9 118
  • 泛型代码可以确保你写出灵活的,可重用的函数和定义出任何你所确定好的需求的类型。你的可以写出避免重复的代码,并且用一...
    iOS_Developer阅读 800评论 0 0
  • 不知何时开始流行:人生像是一场修行,追寻更好的自己。然而可笑的是,勾起的完美微笑下, 却是丢失的自己。
    舒泠阅读 307评论 0 0
  • 大家好,我是鹰子。 今天我给大家分享的是一本非常实用的沟通工具书《沟通圣经》。这本书是英国的尼基·斯坦顿写的,截止...
    鹰子的笔记本阅读 335评论 0 0
  • 懂得保护自己的界限,懂得欣赏自己的性别,懂得在青春期合适的面对性冲动,并且等候。这便是我们对子女性教育的最大目的。...
    溪边小树阅读 179评论 1 0