程序猿阵线联盟-汇总各类技术干货禅与计算机程序设计艺术我爱编程

Kotlin:泛型杂谈(上)

2018-06-18  本文已影响53人  泪已无痕

Kotlin泛型抛弃语法上的差异,在一般情况下与Java都是相近的,所以在这里简单介绍一下Kotlin泛型中独有的东西。

  1. 泛型扩展属性:
val <T>  List<T>.penultimate: T
    get() = this[size - 2]   

>>> println(listof(1, 2, 3, 4).penultimate) 

通过上面的例子可以看出,泛型扩展属性的实现相当简单,这里就不做过多的讲解,但是有一点需要注意的是:由于不能在一个类的属性中存储多个不同类型的值,因此普通属性(即类的非扩展属性)不能声明为泛型。

  1. 非空约束:
class A<T>  {
    fun doSomething(value: T) {
        value?.hashCode()
    }
}

value?.hashCode()中我们可以得知,参数value是可空的,所以我们需要对它进行安全调用,也就是说类型T的默认上界为Any?,那如果我们想要保证类型T永远为非空类型怎么办呢?我们可以对上述代码做以下改造:

class A<T : Any> {
    fun doSomething(value: T) {
        value.hashCode()
    }
}

对,很简单,我们只要将类型T的默认上界改为Any即可。

  1. 实例化类型参数

相信大家都知道,在Java中,泛型类实例的类型参数在运行时是不保留的,也就是说对于如List<Integer>List<String>这样的类型声明在运行时得到的都是同样的List,我们无法从List中识别出它里面包含的究竟时Integer还是String,这样的现象我们称之为类型擦除。也正是因为此,我们想要用Java实现下面的功能是不可能的:

class TypeCheck<T> {

    boolean isMe(Object value) {
        return value instanceof T;
    }
}

public class Demo {

    public static void main(String[] args) {
        TypeCheck<String> stringCheck = new TypeCheck<>();
        System.out.println(stringCheck.isMe("HELLO WORLD"));
    }
}

上面是Java的表现,那么在Kotlin又是如何呢?让我们把上面的例子翻译为Kotlin:

class TypeCheck<T> {
    fun isMe(value: Any): Boolean {
        return value is T
    }
}

fun main(args: Array<String>) {
    val stringCheck = TypeCheck<String>()
    println(stringCheck.isMe("HELLO WORLD"))
}

如果我们尝试编译上面的代码,我们将得到以下错误:

Error:(3, 25) Kotlin: Cannot check for instance of erased type: T

通过对比,我们发现不论在Java中还是在Kotlin中,泛型类实例的类型参数在运行时都是不保留的(即发生了类型擦除),那这是否意味着在Kotlin中我们也无法实现这种类型检查的功能呢?答案当然是否定的,要想在Kotlin的运行时中引用泛型参数实际的类型实参,唯一的方式是使用内联函数。首先让我们首先看下面的例子:

inline fun <reified T> isMe(value: Any): Boolean {
    return value is T
} 

fun main(args: Array<String>) {
    println(isMe<String>("HELLO WORLD"))
}

编译运行,我们发现这次成功运行了,并且输出为true。那这究竟是什么鬼呢?在Kotlin:关于内联函数的一些理解中,我们可以得知,编译器会用内联函数的实际代码来替换每一次的函数调用,每次我们调用带实化类型参数(即在内联函数中被reified关键字修饰的类型参数)的函数时,编译器能够获知该次调用中类型参数的具体类型,因此上述main函数实际被编译成以下样子:

fun main(args: Array<String>) {
    println("HELLO WORLD" is String)
}

很简单对不对?让我们对实例化类型参数进行一个简单的总结:

上面我们从:泛型扩展属性、非空约束、实例化类型参数三个方面简单介绍了一下Kotlin中泛型相对于Java独有的特性,那么除此之外是否还有别的特性呢?且听下回分解。^ _ ^

上一篇下一篇

猜你喜欢

热点阅读