Kotlin泛型-运行时候的类型擦除和使用inline具像化泛型

类型擦除

泛型在编译后类型会被擦除,所以在程序运行时候我们是不知道泛型的信息的。这里不知道泛型的信息听起来可能会有点抽象,我举个例子,在声明泛型List< String >的时候,编译后它的String类型会擦除,运行时候它仅仅是一个List, 我们并不能知道这个List里面放的是string对象.

例如:

val list1: List<String> = listOf("a", "b") 
val list2: List<Int> = listOf(1, 2, 3)

在编译后实际运行中

1.png

它们仅仅是一个List.

这样的类型擦除会造成一个问题,就是我们在写代码时候不能确定泛型是哪个泛型. 例如:

>>> if (value is List<String>) { ... }
ERROR: Cannot check for instance of erased type

因为类型擦除的原因,导致运行时候不能确认它是一个String的List.

Kotlin不允许使用没有指定类型参数的泛型。那我们怎么去检查一个对象是否是一个List呢,这里可以用星投射<*>, 例如

if (value is List<*>) { ... }

不过这里你仍然可以使用as和as?进行强制类型转换, 即使这种强转有对应的基础类型,但是类型参数不对也不会失败,因为当运行时强转这个类型参数是未知的,这个时候它只是给一个警告“unchecked cast”。

fun printSum(c: Collection<*>) {
    val intList = c as? List<Int>
            ?: throw IllegalArgumentException("List is expected")
    println(intList.sum())
}

fun main(args: Array<String>) {
    printSum(listOf(1, 2, 3))
}

这段代码是能够编译通过的,运行时候我们按照下面测试就会出现对应的异常

>>> printSum(setOf(1, 2, 3))                  
IllegalArgumentException: List is expected
>>> printSum(listOf("a", "b", "c"))          
ClassCastException: String cannot be cast to Number

这里我们来分析一下printSum(listOf("a", "b", "c"))为什么会抛出ClassCastException

首先我们之前提到了,编译后会进行泛型擦除,那么这里虽然是一个string list强转List< Int >也不会有什么问题,接下来调用intList.sum()就会抛出ClassCastException了,因为string 不能 强转为int.

那如果我改下printSum实现是可以防止这种现象发生的

fun printSum(c: Collection<Int>) {
    if (c is List<Int>) {
        println(c.sum())
    }
}

fun main(args: Array<String>) {
    printSum(listOf(1, 2, 3))
}


在这里我们提供足够的参数类型,保证编译以前确保对应的泛型,使它能够调用is确定是List< Int >类型.

使用inline具像化泛型

上面提到过泛型编译后会进行类型擦除,这导致我们在函数里面不能够确定泛型参数的类型,例如:

>>> fun <T> isA(value: Any) = value is T
Error: Cannot check for instance of erased type: T

这种场景想要实现,我们可以通过inline函数具像化参数.

inline fun <reified T> isA(value: Any) = value is T

fun main(args: Array<String>) {
    println(isA<String>("abc"))
    println(isA<String>(123))
}

之所以上面的写法能够成立,因为inline方法在编译时候会被替换成实际的执行代码,准确来讲它就不是一个函数,这样在编译时候是能够确认reified T的类型的. 下面再举个更实际的例子,更加容易理解inline具象化泛型的神奇之处. kotlin集合的扩展函数有一个filterIsInstance,用来过滤集合中的某个类型。

fun main(args: Array<String>) {
    val items = listOf("one", 2, "three")
    println(items.filterIsInstance<String>())
}

如果没有inline具象化泛型,这个功能是做不了的,因为类型擦除后,在filterIsInstance是判断不了泛型到底是什么类型的,不过利用inline具象化我们看看filterIsInstance的实现

inline fun <reified T>                          
        Iterable<*>.filterIsInstance(): List<T> {
    val destination = mutableListOf<T>()
    for (element in this) {
        if (element is T) {                    
            destination.add(element)
        }
    }
    return destination
}

这里我们可以判断参数是否是泛型T了。

为什么使用inline后可以具象化泛型了?

因为使用inline标记的函数,它和普通函数是有区别的,我们知道kotlin函数实际上也是一个类,每个函数会编译成一个对象,但是inline函数在编译的时候会直接编译成字节码,不会生成函数对象,这样实际上就不存在函数使用泛型参数的情况了。

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

推荐阅读更多精彩内容