【Android基础】第一行代码-泛型高级特性
泛型实化概念
Java的泛型功能是通过类型擦除机制来实现的。泛型对于类型的约束只在编译时期存在。例如,假设我们创建了一个List集合,虽然 在编译时期只能向集合中添加字符串类型的元素,但是在运行时期JVM并不能知道它本来只打算 包含哪种类型的元素,只能识别出来它是个List。
Kotlin提供了一个内联函数的概念,内联函数中的代码会在编译的时候自动被替换到调用它的地方,这样的话也就不存 在什么泛型擦除的问题了,因为代码在编译之后会直接使用实际的类型来替代内联函数中的泛型声明。


bar()是一个带有泛型类型的内联函数,foo()函数调用了bar()函数,在代码编 译之后,bar()函数中的代码将可以获得泛型的实际类型。
具体该怎么写才能将泛型实化呢?
首先,该函数必须是内联函数才行,也就是要用inline 关键字来修饰该函数。其次,在声明泛型的地方必须加上reified关键字来表示该泛型要进行 实化

这里 我们准备实现一个获取泛型实际类型的功能:
![]()
getGenericType()函数直接返回了当前指定泛型的实际类型。T.class这样的语法在Java 中是不合法的,而在Kotlin中,借助泛型实化功能就可以使用T::class.java这样的语法了。
对getGenericType()函数进行测试:

里给getGenericType()函数指定了两种不同的泛型,由于getGenericType()函数会将 指定泛型的具体类型返回,因此这里我们将返回的结果进行打印。

泛型实化应用

这里我们定义了一个startActivity()函数,该函数接收一个Context参数,并同时使用 inline和reified关键字让泛型T成为了一个被实化的泛型。

Intent接收的第二个参数本来应该是一个具体Activity的Class类型,但由于现在T已经是一个 被实化的泛型了,因此这里我们可以直接传入T::class.java。最后调用Context的 startActivity()方法来完成Activity的启动。 现在,如果我们想要启动TestActivity,只需要这样写就可以了:
![]()
不过,现在的startActivity()函数其实还是有问题的,因为通常在启用Activity的时候还可 能会使用Intent附带一些参数。而经过刚才的封装之后,我们就无法进行传参了。这里添加一个新的startActivity()函数重载:

这次的startActivity()函数中增加了一个函数类型参数,并且它的函数类型是 定义在Intent类当中的。在创建完Intent的实例之后,随即调用该函数类型参数,并把Intent的 实例传入,这样调用startActivity()函数的时候就可以在Lambda表达式中为Intent传递 参数了

泛型的协变
一个泛型类或者泛型接口中的方法, 它的参数列表是接收数据的地方,因此可以称它为in位置,而它的返回值是输出数据的地方,因 此可以称它为out位置

假如定义了一个MyClass<T>的泛型类,其中A 是B的子类型,同时MyClass<A>又是MyClass<B>的子类型,那么我们就可以称MyClass在T 这个泛型上是协变的。
但是如何才能让MyClass<A>成为MyClass<B>的子类型呢?
如果一个泛型类 在其泛型类型的数据上是只读的话,那么它是没有类型转换安全隐患的。而要实现这一点,则 需要让MyClass <T>类中的所有方法都不能接收T类型的参数。换句话说,T只能出现在out位置 上,而不能出现在in位置上。


在泛型T的声明前面加上了一个out关键字。这就意味 着现在T只能出现在out位置上,而不能出现在in位置上,同时也意味着SimpleData在泛型T上 是协变的。 由于泛型T不能出现在in位置上,因此我们也就不能使用set()方法为data参数赋值了,所以这 里改成了使用构造函数的方式来赋值。构造函数中的泛型T不也是在in位置上的 吗?没错,但是由于这里我们使用了val关键字,所以构造函数中的泛型T仍然是只读的,因此 这样写是合法且安全的。另外,即使我们使用了var关键字,但只要给它加上private修饰 符,保证这个泛型T对于外部而言是不可修改的,那么就都是合法的写法。
泛型的逆变
假如定义了一个 MyClass的泛型类,其中A是B的子类型,同时MyClass又是MyClass的子类型, 那么我们就可以称MyClass在T这个泛型上是逆变的。


这里我们在泛型T的声明前面加上了一个in关键字。这就意味着现在T只能出现在in位置上,而 不能出现在out位置上,同时也意味着Transformer在泛型T上是逆变的。
更多推荐



所有评论(0)