前言
Kotlin
是一种在 Java
虚拟机上运行的静态类型编程语言,被称之为 Android
世界的Swift
,在Google
I/O 2017中,Google
宣布 Kotlin
成为 Android
官方开发语言
reified关键字的作用
reified
意为具体化,是kotlin所增强的一种泛型的使用方式,需要配合inline关键字使用
Java中的泛型
Java
中我们使用泛型类型有泛型类,泛型接口,泛型方法,主要的目的就是去封装一个模板,让模板满足多种类型情况的调用,比如我们常用的集合类,不同的集合可以有不同的元素类型,比如:String
,Person
,NewsItem
等,他们都会有相同的增 删 改 查等操作,唯一不同的就是元素类型不同,这种情况下就可以 将其类型抽离出来,将类型作为参数传入,这种就是典型的使用泛型类的方式
public class ArrayList{}
再有例子就是我们定义了进行元素比较的接口,接口可能会用于不同类型的元素进行比较,则可以定义泛型接口,然后 在不同的实现类指定具体的泛型类型,这种就是典型的使用泛型接口的方式
public interface Compartor{
int compare(T o1, T o2);
}
一个函数也一样,如果我们一个函数会有不同的类型的参数调用,也可以将其定义为泛型方法
public T findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
泛型的类型擦除
在Java
中泛型的实现方式是通过编译器擦除泛型类型来实现的,即运行时不带任何泛型类型相关的信息,比如ArrayList
在运行时候只是用ArrayList
来运行,只不过在读取的时候将Object
转换为String
public void main() {
ArrayList list = new ArrayList<>();
list.add("123");
String value = list.get(0);
}
转化为字节码
0 new #2
3 dup
4 invokespecial #3 >
7 astore_1
8 aload_1
9 ldc #4 <123>
11 invokevirtual #5
14 pop
15 aload_1
16 iconst_0
17 invokevirtual #6
20 checkcast #7
23 astore_2
24 return
在第20行将其转换成了String
,也就是说其实不同的泛型类型执行的时候调用的都是一份代码,只不过在使用数据的时候多了一份转换,所以下面的写法是不允许的,所以是无法通过泛型类型拿到泛型类型的相关信息
public void test(T t) {
Class c=T.class;//提示错误
if (t instanceof String) {
T t1 = new T() //提示错误
}
}
使用reified关键字
- 使用
reified
修饰函数中的泛型类型声明 - 使用
inline
修饰函数使之内联
省略参数中的Class对象
如果你的泛型方法的参数含有Class
对象,因为泛型函数中是无法获取到class对象的,所以需要
fun test(t:T,clazz: Class) {
val c = T::class.java //报错
// 因为无法从T拿到T的反射,需要传入参数clazz
}
使用reified
关键字修饰
inline fun test(t:T) {
val c = T::class.java //编译成功
}
inline fun test(t: T, v: V) {
val c = T::class.java //编译成功
val c1 = V::class.java //编译失败
}
inline
修饰函数表示内联函数,会将泛型类型内嵌到编译代码中替换成具体类型,配合reified
关键字使用就可以在泛型函数中获取到反射类型的Class
对象
inline fun Activity.startActivity(context: Context)
= startActivity(Intent(context, T::class.java))
startActivity(context)
可以再看一个案例,比如我们需要封装在Fragment
下的公共获取传参的参数,由于我们需要在方法内部判断获取数据的类型,所以需要将Class
对象传入,因为默认是拿不到泛型的反射类型的
fun Fragment.getValue(key: String, clazz: Class) =
when (clazz) {
java.lang.String::class.java -> {
arguments?.getString(key)
}
java.lang.Long::class.java -> {
arguments?.getLong(key)
}
else -> {
null
}
}
使用reified
优化
inline fun Fragment.getValue(key: String) =
when (T::class) {
String::class -> {
arguments?.getString(key)
}
Long::class -> {
arguments?.getLong(key)
}
else -> {
null
}
}
或者我们在一些特殊的额情况下需要创建泛型类型的实例
fun test()= T::class.java.newInstance() //编译错误,无法拿到Class
inline fun test()= T::class.java.newInstance() //编译成功
Array中的使用
因为需要我们需要定义一个数组分割某个子元素的函数,以便方便使用,我们可以这样
会有编译错误提示Can't use T as a reified type parameter
fun splitArray(array: Array, item: T) =
//这里会编译报错,提示Can't use T as a reified type parameter
Array(array.size - if (array.contains(item)) 1 else 0) {
if (it >= array.indexOf(item)) {
array[it + 1]
} else {
array[it]
}
}
这种情况是因为,Array
在编译时候会被编译成java T[]
,所以也需要将其进行实化
inline fun splitArray(array: Array, item: T) =
Array(array.size - if (array.contains(item)) 1 else 0) {
if (it >= array.indexOf(item)) {
array[it + 1]
} else {
array[it]
}
}
欢迎关注Mike的
Android 知识整理