Kotlin进阶 - 泛型

在Android开发中我们经常用到泛型,如:List、Map、Set、Adapter等,那么在Kotlin中同样支持泛型。
什么是泛型呢?
泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。

一、Kotlin中定义泛型方式

在Kotlin中定义泛型有跟Java一样,有以下两种方式:

  • 定义在类上
  • 定义在函数中
    定义在类上示例:
class MagicBox<T>(val item: T) {
    var available = false

    fun fetch(): T? {
        return item.takeIf { available }
    }
}

定义在函数中

class MagicBox<T>(val item: T) {
    var available = false

    /**
     * 函数中增加范式类型,类似Java
     */
    fun <R> fetch(anotherGenericity: (T) -> R): R? {
        return anotherGenericity(item).takeIf { available }
    }
}

二、Kotlin中定义约束泛型

Kotlin中定义约束泛型与Java < T extend XXX>定义泛型的方式类似,表示泛型指定的类型必须为指定类型的类或继承了指定类型类的子类。这句话有点绕,举个例子:定义一泛型class A<T : B>(),传入的泛型T必须为B或者B的子类。完整示例如下:

//定义一个约束泛型
private class ConstraintMagicBox<T:Human>(item:T){

}

//定义一个父类
private open class Human(val name: String,val age:Int)
//定义一个Human类的子类
private class Male(name: String,age: Int):Human(name, age)
//定义一个Human类的子类
private class Female(name: String,age: Int):Human(name, age)

fun main() {
    //同为Human类型的可传入
    val male = ConstraintMagicBox(Male("Jack", 20))
    val female = ConstraintMagicBox(Female("Jim", 20))
}

三、Kotlin定义数量可变参数泛型

这点类似Java中给方法定义可变参数(private void method(int.. num)),在Kotlin中定义方式要借助关键字**vararg**,普通函数和构造函数均可使用。
示例如下:

private class VarMagicBox<T : VarHuman>(vararg val items: T) {

    //根据参数从items中获取数据,其中items类型未Array
    fun fetch(index: Int): T? {
        return items.getOrNull(0)
    }

    //在函数中增加可变参数
    fun fetch(vararg indexs: Int):List<T>{
        indexs.takeIf {
            indexs.isNotEmpty()
        }.run {
            return items.filterIndexed { index, t ->
                indexs.contains(index)
            }
        }
    }
}

private open class VarHuman(val name: String, val age: Int)

四、Kotlin中用in、out修饰泛型

在Java中在定义一个List中,指定一个特定类型,创建实例时只能new该类型的实例,无法new出子类或者父类的实例,
例子:
ArrayList<String> list = new ArrayList<CharSequence>()
或者
ArrayList<CharSequence> list = new ArrayList<String>()
这两种写法在JAVA是不支持的,正确的写法为:ArrayList<String> list = new ArrayList<String>()
但是在Kotlin中可以做到支持
当然针对上面定义的泛型例子同样不支持,那么Kotlin如何才能支持这样的写法呢?
因为Kotlin中有两个重要的关键字in(协变)out(逆变)
下面我们就来看下者两个关键字如何做到支持上述写法的。
用法非常简单,先上示例,看下使用方式:

//out修饰的泛型 仅将泛型作为函数返回值
//作用:让子类泛型对象可以赋值给父类泛型对象
interface OutTest<out T>{
    fun outTest():T
}

//in修饰的泛型 仅将泛型作为函数参数,泛型无法当做返回值
//作用:让父类泛型对象可以赋值给子类泛型对象
interface InTest<in T>{
    fun inTest(param : T)
}

测试代码如下:

open class Food()
open class FastFood():Food()
class Hamburg():FastFood()

class FastFoodStore() : OutTest<FastFood>{
    override fun outTest(): FastFood {
        println("FastFoodStore ----------")
        return FastFood()
    }
}

class HamburgStore():InTest<FastFood>{
    override fun inTest(param: FastFood) {
        println("HamburgStore-----------")
    }
}

fun main() {
    //子类对象可以传给父类泛型对象 out
    val food1 : OutTest<Food> = FastFoodStore()

    //父类对象可以传给子类泛型对象 in
    val food2 : InTest<Hamburg> = HamburgStore()
}

关键字in、out使用总结
主要有两点:

  • out修饰的泛型只能在函数返回值中使用,in修饰的泛型只能在函数的参数中使用;
  • out修饰的泛型只能将子类泛型对象赋值给父类泛型对象in修饰的泛型只能将父类泛型对象赋值给子类泛型对象,如下图示;
    image.png

欢迎留言大家互相交流学习!


示例源码地址kotlin_demo

猜你喜欢

转载自blog.csdn.net/seevc/article/details/123007514