【Kotlin】笔记22-泛型的高级特性

Kotlin笔记22-泛型的高级特性,reified

14.1 泛型的高级特性,reified

  • 对泛型进行实化

reified

Kotlin中是可以将内联函数中的泛型进行实化的

  1. 声明函数必须是内联函数(inline)

  2. 在声明泛型的地方必须加上reified关键字来表示该泛型要进行实化

示例代码如下:

inline fun <reified T> getGenericType() = T::class.java

这里实现了个Java中完全不可能实现的功能: geGenericType()
函数直接返回了当前指定泛型的实际类型T.Class这样的语法在Java中是不合法的,而在Kotlin
中,借助泛型实化功能就可以使用T::class.java这样的语法了.

举个栗子:

fun main() {
	val result1 = genericType<String>()
	val result2 = genericType<Int>()
	println("result1 is $result1")
	println("result2 is $result2")
}

结果:

result1 is class java.Lang.String
result2 is class java.Lang.Integer

  • 泛型实化的应用
序号 Tips
1 泛型实化功能允许我们在泛型函数当中获得泛型的实际类型,这也就使得类似于a is T, T::class.java 这样的语法成为了可能.

举个栗子:

val intent = Intent(context, TestActivity::class.java)
context.startActivity(intent)

优化:

inline fun <reified T> startActivity(context: Context) {
	val intent = Intent (context,T::class.java)
	context.startActivity(intent)
}

使用:

startActivity<TestActivity>(context)

举个栗子2:

通常在启用Activity的时候还可能会使用Intent附带些参数

val intent = Intent(context, TestActivity::class.java)
intent.putExtra("param1", "data")
intent.putExtra("param2", 123)
context.startActivity(intent)

优化:

inline fun <reified T> startActivity(context: Context, block: Intent.() -> Unit) (
	val intent = Intent(context, T::class.java)
	intent.block()
	context.startActivity(intent)
}

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

startActivity<TestActivity>(context) {
	putExtra("param1", "data")
	putExtra("param2", 123)
}
  • 泛型的协变

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

interface MyClass<T>{
	fun method(param: T in位置 ): T out位置
}

in: 泛型的协变: 假如定义了一个MyClass的泛型类, 其中A是B的子类型, 同时MyClass < A>又是MyClass< B>的子类型, 那么我们就可以称MyClass在T这个泛型上是协变的.

举个栗子:

class SimpleData<T> {
	private var data: T? = null
	
	fun set(t: T?) { data = t }
	fun get(): T? { return data }
}

修改:

class SimpleData<out T>(val data: T?) (
	fun get(): T? {
	return data
}
  • 泛型的逆变

out 定义: 假如定义了一个MyClass< T>的泛型类, 其中A是B的子类型, 同时MyClass< B>又是MyClass< A>的子类型, 那么我们就可以称MyClass在T这个泛型上是逆变的.

协变和逆变的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y22pjIuy-1649926891761)(C:\Users\mozhimen\AppData\Roaming\Typora\typora-user-images\image-20220414162350277.png)]

举个栗子:

interface Transformer<T> {
	fun transform(t: T): String
}

fun main() {
	val trans = object : Transformer<Person> {
		override fun transform(t: Person): String {
			return "${t.name} ${t.age}"
		}
	}
	handleTransformer(trans) //这行代码会报错
}

fun handleTransformer(trans: Transformer<Student>) {
	val student = Student("Tom", 19)
	val result = trans.transform(student)
}

这段代码在调用handleTransformer()方法的时候却会提示语法错误, 这里Transformer并不是 Transformer的子类型. 那么这个时候逆变就可以派上用场了, 它就是专门用于处理这种情况的。修改Transformer接口中的代码:

优化:

interface Transformer<inT> {
	fun transform(t: T): String
}

@UnsafeVariance注解可以打破, 让泛型在协变时只出现在out位置, 逆变时只出现在in位置这一语法
规则, 但同时也会带来额外的风险, 所以你在使用@UnsafeVariance注解时, 必须很清楚自己在干什么才行.

  • 逆变功能在Kotlin内置API中的应用

Comparable 是一个用于比较两个对象大小的接口:

举个栗子:

interface Comparable<in T> {
	operator fun compareTo(other: T): Int
}

Comparable在T这个泛型上就是逆变的, compareTo()方法则用于实现具体的比较逻辑. 那么这里为什么要让Comparable 接口是逆变的呢?想象如下场景,如果我们使用Comparable实现了让两个Person对象比较大小的逻辑,那么用这段逻辑去比较两个Student对象的大小也一定是成立的, 因此让Comparable成为Comparable的子类合情合理.

猜你喜欢

转载自blog.csdn.net/weixin_42473228/article/details/124176414
今日推荐