Kotlin系列五:泛型及其高级特性

859 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一 泛型

1.1 泛型类

class MyClass <T>{
    fun method(param :T): T {
        return param
    }
}
val myClass =MyClass<Int> ()
val result = myClass.method(123)

1.2 泛型方法

class MyClass {
    fun <T> method(param :T): T {
        return param
    }
val myClass =MyClass ()
val result = myClass.method<Int>(123)

//Kotlin出色的类型推导机制
val myClass =MyClass ()
val result = myClass.method(123)

实践举例:完成一个类似apply扩展函数的build功能

fun <T> T.build(block:T.() ->Unit):T{
        block
        return this
    }

1.3 限制泛型类型

举例:将泛型上界设置为Number 类型(将method方法的泛型指定成数字类型)

class MyClass {
    fun <T :Number> method(param :T): T {
        return param
    }
}

默认情况下,所有泛型都是可以指定成可空类型的,这是因为在不动手指定上界的时候,泛型的默认上界是Any?;如果想让泛型的类型不可为空,只需将上界指定成Any 。

二 类委托和委托属性

委托是一种设计模式 ,它的基本理念是 :操作对象自己不会去处理某段逻辑,而是把工作委托给另外一个辅助对象去处理。

好处: 如果一个类,让大部分的方法实现都是调用辅助对象中的方法,而少部分的方法是自己实现的。这就是委托模式的意义所在。

2.1 类委托

类委托核心思想:将一个类的具体实现委托给另一个类去完成。借助于委托模式,我们可以轻松实现一个自己的实现类。如:定义一个MySet,并让他实现Set接口:

class MySet<T>(val helperSet:HashSet<T>):Set<T>{
    override val size: Int
        get() = helperSet.size
    override fun contains(element: T): Boolean = helperSet.contains(element)
 
    override fun containsAll(elements: Collection<T>): Boolean  = helperSet.containsAll(elements)
 
    override fun isEmpty(): Boolean = helperSet.isEmpty()
 
    override fun iterator(): Iterator<T> = helperSet.iterator()
 
}
 
//下面代码相当于上面代码,将操作委托给helperSet
class MySet<T>(val helperSet:HashSet<T>):Set<T> by helperSet{
 
}

这其中接收的一个HashSet参数,这就相当于一个辅助对象;然后在Set接口中的所有的方法的实现中,我们都没有进行自己的实现,而是调用了辅助对象中的相应的方法实现。这就是一种委托模式。

但是如果有成百上千的返方法都要如上代码中那样去调用辅助对象中的相应代码实现,就要哭了。 为了避免这种调用对象中的待实现方法过多的情况,使用的关键字 by加上受委托的辅助对象进行类委托,这样可以省却很多的模板代码编写。

//MySet中有所有Set接口中的功能,和HashSet保持一致, 并且isEmpty是自己实现的
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {
 
    fun hello() = println("hello")
 
    //如果你想重写set的某个方法,还能自己重写 ,不想就委托HashSet的用它的
    override fun isEmpty(): Boolean {
        return true
    }
 
}

2.2 委托属性

委托属性 将一个属性(字段)的具体实现委托给另外一个类去完成委托属性的语法结构:

class MyClass{
    var p by Delegate()
}

lazy函数懒加载技术原理:通过by委托属性然后在调用获取值的时候才调用getValue

三 泛型的高级特性

3.1 泛型实化

java和kotlin都是类型擦除机制,泛型只是对于编译器的类型的约束,运行期是识别不出来我们代码中指定的泛型类型的,所以肯定实现不了 a is T 或者 T::class.java 这样的用法。

但是Koltlin可以利用内联函数和reified关键字将内联函数中的泛型进行实化。例如:inline fun getGenricType() = T::class.java

泛型实化的应用:

//用到了内联函数实化,扩展函数,高阶函数
inline fun <reified T> startActivity(context:Context,block:Intent.() -> Unit){
    val intent = Intent(context,T::class.java)
    intent.block()
    context.startActivity()
}
 
//用到了lambda表达式
startActivity<TestActivity>(this){
    putExtra("param1","data")
    putExtra("param2",123)
}

面试问题:为什么Kotlin可以泛型实例化?

泛型实例化要求必须是inline修饰的内联函数中的泛型,而内联函数在进行代码替换后,替换后的代码中是可以获得泛型的实际类型的(因为你泛型T本就是写在了内联函数fun xxx 中的T中)。这时你还用refield关键字来表示要对该泛型进行实例化,当然就可以获得该泛型具体的类型并实例化了。

3.2 泛型的协变

java中不允许List是List的子类,原因如下:

class SimpleData<T> {
    
    private var data:T? = null
 
    fun set(t:T?){
        data = T
    }
 
    fun get():T = data
}
 
val student = Student()
val data = SimpleData<Student>()
data.set(student)
val teacher = Teacher()
data.set(teacher)
val studentData = data.get()//此处就存在隐患,不知道是Student还是Teacher

协变:假如A是B的子类型,则MyClass也是MyClass的子类型

实现如下:利用out关键字

class SimpleData<out T>(val data:T) {
 
    fun get():T = data
 
}

3.3 泛型的逆变

如果A是B的子类型,则MyClass是MyClass的子类型

interface Transformer<T> {
    fun transform(t:T):String
}
 
fun main(){
    val trans = object : Transtormer<Person> {
        override fun transform(t:Person):String = "${t.name}${t.age}"
    }
    handleTransformer(trans)//会报错,因为Transtormer<Person>不是Transtormer<Student>子类
}
 
fun handleTransformer(t:Transtormer<Student>){
    val student = Student()
    val result = t.transform(student)
}
 
//逆变修改如下
interface Transformer<in T> {
    fun transform(): @UnsafeVariance T
}
 
fun main(){
    val trans = object : Transtormer<Person> {
        override Teacher()
    }
    handleTransformer(trans)//通过编译,但是写法不对
}
 
fun handleTransformer(t:Transtormer<Student>){
    val student = Student()
    val result = t.transform(student)
}

具体源码例子,Comparable用于对比两个对象大小的接口

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

主要例子和参考

郭霖《第一行代码》 Kotlin部分