本文灵感来源:群友提出泛型相关的问题,感受不少人对泛型并非很了解~
Kotlin中的泛型和Java中的泛型其实大同小异,只是语法稍微有些不一样。
大部份内容摘取自:《Kotlin实用指南》,感兴趣的能够订阅一波 ~java
泛型可用于类、接口和方法的建立,对应:泛型类,泛型接口和泛型方法,代码示例以下:web
注意事项:安全
- 一、泛型的参数只能是「类类型」,不能是简单数据类型(int, float等);
- 二、泛型能够有多个泛型参数
Tips:泛型类型的命名不是必须为T,也可使用其余「单个大写字母」,没有强制的命名规范,但为了便于阅读,有一些约定成俗的命名规范:app
- 通用泛型类型:T,S,U,V
- 集合元素泛型类型:E
- 映射键-值泛型类型:K,V
- 数值泛型类型:N
和C#中的泛型不一样,Java和Kotlin中的泛型都是假泛型,实现原理就是「类型擦除(Type Erasure)」。
Java编译器在生成Java字节码中是不包含泛型中的类型信息的,只存在于代码编译阶段,进JVM前会被擦除。
不信?写个简单的代码体验下:函数
运行结果以下:学习
从输出结果能够看到得到的类型确实被擦除了,此时的「类类型」皆为ArrayList;spa
问 → 那咱们定义的泛型类型(Integer, String)到底去哪了?
答 → 被替换成了「原始类型」(字节码中的真正类型)
问 → 那原始类型具体是什么类型?
答 → 「限定类型」,无限定的话都用Object替换。
问 → ???
答 → 且听我娓娓道来~.net
仍是上面的代码,进Java字节码看看(View -> Show Bytecode)3d
的确,Integer和String都被替换成了Object,那这个「限定类型」呢?写个例子试试:code
看下字节码:
行吧,此时的「原始类型」为「限定类型」即Animal类。
没限定类型的,都替换成Object类,也使得咱们能够经过一些操做,绕过泛型。
好比,咱们利用「反射」往Integer类型的List插入一个String值是不会报错的:
运行结果以下:
若是不对泛型类型作限制,泛型类型可实例化为任意类型的话,可能会产生某些安全隐患。为了限制容许实例化的泛型类型,可在泛型类型后追加 extends 父类型,代码示例以下:
有界泛型类型在必定程度上限制了泛型类型,提升了程序的安全性;由于定义了边界,因此能够调用父类或父接口的方法。
Java泛型自己「不变」的,不支持「协变」和「逆变」,是经过通配符(?)来实现的
<T>「泛型标识符」用于泛型「定义时」可理解为「形参」;
<?>「通配符」用于泛型「实例化时」可理解为「实参」。
代码示例以下:
代码示例以下:
上边界通配符只能读不能写,改成下边界通配符
Tips:看完这里,估计有部分读者仍是有点懵逼,这里总结下:
- 不变 → 子类和父类不要紧,正常存取,不用通配符就好,Java集合自己就是不变的;
- 协变 → A是B的子类型,泛型<A>也是泛型<B>的子类型,只想取,用extends。
- 逆变 → A是B的子类型,泛型<B>也是泛型<A>的子类型,只想存,用super。
- PECS法则(Producer Extends,Consumer Super) 参数化类型是一个生产者,则使用:<? extends T> 若是它是一个消费者,则使用<? super T>
和Java泛型同样,Kotlin中的泛型也是「不变的」,没有「通配符类型」,但有两个其余的东西:「声明处型变」(declaration-site variance) 与 「类型投影」(type projections)
其实就是用「out」和「in」关键字来替换
其实就是对应Java中的*通配符:
- Java中<?>等同于<* extends Object>
- Kotlin中<*>等同于out Any
在Kotlin中能够经过下述四种方法获取泛型的类型(前两种Java也适用):
原理:匿名内部类的声明在编译时进行,实例化在运行时进行。代码示例以下:
获取运行时泛型参数类型,子类可得到父类泛型的具体类型。代码示例以下:
定义一个扩展函数用于启动Activity,代码示例以下:
fun <T: Activity> FragmentActivity.startActivity(context: Context, clazz: Class<T>) {
startActivity(Intent(context, clazz))
}
// 调用
startActivity(context, MainActivity::class.java)
复制代码
Kotlin中使用「inline」关键字定义一个内联函数,配合「reified」具体化(类型不擦除),获得使用泛型类型的Class。修改后的代码示例以下:
inline fun <reified T : Activity> Activity.startActivity(context: Context) {
startActivity(Intent(context, T::class.java))
}
// 调用
startActivity<MainActivity>(context)
复制代码
参考文献: