Kotlin泛型 ③ ( 泛型 out 协变 | 泛型 in 逆变 | 泛型 invariant 不变 | 泛型逆变协变代码示例 | 使用 reified 关键字检查泛型参数类型 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin泛型 ③ ( 泛型 out 协变 | 泛型 in 逆变 | 泛型 invariant 不变 | 泛型逆变协变代码示例 | 使用 reified 关键字检查泛型参数类型 )相关的知识,希望对你有一定的参考价值。

文章目录


本章总结 : 使用了 泛型 out 协变 和 泛型 in 逆变 极大的提高了程序的扩展性 ;

  • 泛型 in 逆变 : 使用 in 关键字 , 可以使 父类泛型对象 赋值给 子类泛型对象 ;
  • 泛型 out 协变 : 使用 out 关键字 , 可以使 子类泛型对象 赋值给 父类泛型对象 ;




一、泛型 out 协变



使用 out 关键字 , 可以使 子类泛型对象 赋值给 父类泛型对象 ;

泛型类 中 , 如果只将 泛型类型 作为 函数的返回值 类型 , 则在 声明 泛型参数 类型 时 , 在 泛型参数 前 使用 out 关键字 , 同时 该 泛型类 又称为 生产类 ( 生产接口 ) , 用于生产 泛型类 指定的泛型对象 ;


代码示例 : 在下面的接口中 , 泛型类型 只用于作为 返回值 ;

interface Producer<out T> 
    fun produce(): T





二、泛型 in 逆变



使用 in 关键字 , 可以使 父类泛型对象 赋值给 子类泛型对象 ;

泛型类 中 , 如果只将 泛型类型 作为 函数的参数 类型 , 则在 声明 泛型参数 类型 时 , 在 泛型参数 前 使用 in 关键字 , 同时 该 泛型类 又称为 消费类 ( 消费接口 ) , 用于消费 泛型类 指定的泛型对象 ;


代码示例 : 在下面的接口中 , 泛型类型 只用于作为参数 ;

interface Consumer<in T> 
    fun consume(t: T)





三、泛型 invariant 不变



泛型类 中 , 如果

  • 既将 泛型类型 作为 函数的参数 类型 ,
  • 又将 泛型类型 作为 函数的返回值 类型 ,

则在 声明 泛型参数 类型 时 , 既不使用 in 关键字 , 又不使用 out 关键字 ;


代码示例 : 在下面的接口中 , 泛型类型 即用于作为 返回值 , 又用于作为参数 ;

interface ProducerOrConsumer<T> 
    fun produce(): T
    fun consume(t: T)





四、泛型逆变协变代码示例



泛型类 中 泛型参数子类父类 ,

  • Java 语言中 , 泛型参数 是 子类 的 泛型类对象 , 不可以赋值 泛型参数 是父类 的变量 ;
    • Java 中的泛型对象赋值 , 不存在继承关系 , 是什么类型就是什么类型 , 类型要严格相同 ;
import java.util.ArrayList;

public class HelloAWT 
    public static void main(String[] args) 
        ArrayList<CharSequence> list = new ArrayList<String>();
    

  • Kotlin 语言中 , 泛型参数是 子类 的 泛型类对象 , 可以赋值给 泛型参数 是父类 的变量 , 前提是泛型参数必须使用 out 关键字修饰 ;
    • 使用 in 关键字 , 可以使 父类泛型对象 赋值给 子类泛型对象 ;
    • 使用 out 关键字 , 可以使 子类泛型对象 赋值给 父类泛型对象 ;

下图中 父类范围子类范围 大 ,

  • 如果 使用 in 关键字 , 则 范围大的父类泛型对象 赋值给 范围小的 子类泛型对象 , ( 反之就会报错 )
  • 如果 使用 out 关键字 , 则 范围小的子类泛型对象 赋值给 范围大的 父类泛型对象 ; ( 反之就会报错 )



使用了 泛型 out 协变 和 泛型 in 逆变 极大的提高了程序的扩展性 ;


在下面的代码中 , FoodFactoryProducer<Food> 子类 , 类型正好匹配 ;

    // FoodFactory 是 Producer<Food> 子类 , 类型正好匹配
    val producer: Producer<Food> = FoodFactory();

FastFoodFactoryProducer<FastFood> 子类 , Producer 的泛型参数 FastFoodFood 的子类 , 在 Kotlin 中 , 可以将 Producer<FastFood> 类型赋值给 Producer<Food> 类型 , 在 Java 中这种用法不行 ;

    // FastFoodFactory 是 Producer<FastFood> 子类
    // Producer 的泛型参数 FastFood 是 Food 的子类
    // 在 Kotlin 中 , 可以将 Producer<FastFood> 类型赋值给 Producer<Food> 类型
    // 在 Java 中这种用法不行
    val producer2: Producer<Food> = FastFoodFactory();

代码示例 :

import java.util.*

interface Producer<out T> 
    fun produce(): T

interface Consumer<in T> 
    fun consume(t: T)

interface ProducerOrConsumer<T> 
    fun produce(): T
    fun consume(t: T)


open class Food
open class FastFood : Food()
class Burger : FastFood()

class FoodFactory : Producer<Food> 
    override fun produce(): Food 
        println("生产食物")
        return Food()
    

class FastFoodFactory : Producer<FastFood> 
    override fun produce(): FastFood 
        println("生产快餐")
        return FastFood()
    

class BurgerFactory : Producer<Burger> 
    override fun produce(): Burger 
        println("生产汉堡")
        return Burger()
    


class People : Consumer<Food> 
    override fun consume(t: Food) 
        println("人吃食物")
    

class ModernPeople : Consumer<FastFood> 
    override fun consume(t: FastFood) 
        println("现代人吃快餐")
    

class WestModernPeople : Consumer<Burger> 
    override fun consume(t: Burger) 
        println("西方现代人喜欢吃汉堡")
    


fun main() 
    // I. 泛型 out 协变 , 使用 out 关键字 , 可以使 子类泛型对象 赋值给 父类泛型对象

    // FoodFactory 是 Producer<Food> 子类 , 类型正好匹配
    val producer: Producer<Food> = FoodFactory();
    producer.produce()

    // FastFoodFactory 是 Producer<FastFood> 子类
    // Producer 的泛型参数 FastFood 是 Food 的子类
    // 在 Kotlin 中 , 可以将 Producer<FastFood> 类型赋值给 Producer<Food> 类型
    // 在 Java 中这种用法不行
    val producer2: Producer<Food> = FastFoodFactory();
    producer2.produce()

    // II. 泛型 in 逆变 , 使用 in 关键字 , 可以使 父类泛型对象 赋值给 子类泛型对象
    // People 的类型是 Consumer<Food>
    // consumer 的类型是 Consumer<Burger>
    // 在 Consumer 中 , 使用了泛型参数 in 逆变
    // 泛型参数是父类 的泛型类对象 可以赋值给 泛型参数是子类 的泛型对象
    val consumer : Consumer<Burger> = People()
    consumer.consume(Burger())

执行结果 :

生产食物
生产快餐
人吃食物




五、使用 reified 关键字检查泛型参数类型



泛型参数类型 T运行时 会被 类型擦除 , 因此 在运行时 是 不知道 泛型参数 的 具体类型 的 ,

借助 reified 关键字 可以检查 运行时 泛型参数 的 具体类型 ;


在 Java 中 , 运行时 不知道 泛型参数 的 具体类型 ; 在 Kotlin 中可以 通过 reified 关键字检查 泛型参数类型 ;


Java 中如果想要知道 泛型参数 具体类型 , 通过常规的方法无法实现 , 通过 反射 可以实现 ;

Java 泛型类对象.javaClass.name == "要判断的类的全类名"

在 函数 中 使用 reified 关键字 , 需要在 尖括号 <> 中 泛型类型 之前 添加 reified 关键字 , 此外 函数 还要 使用 inline 关键字 进行修饰 ;

inline fun <reified T> 函数名(t: T): T 

使用了 reified 关键字 修饰 泛型 inline 函数 中 , 可以 使用 is 判定 泛型参数的具体类型 ;


代码示例 :

open class Food
open class FastFood : Food()
class Burger : FastFood()

class Student<T: Food> () 
    inline fun eat(food: T) 
        if (food is Burger) 
            println("吃汉堡")
         else if (food is FastFood) 
            println("吃快餐")
         else if (food is Food) 
            println("吃普通食物")
        
    


fun main() 
    val student = Student<Food>()
    student.eat(Food())

    val student2 = Student<FastFood>()
    student2.eat(FastFood())

    val student3 = Student<Burger>()
    student3.eat(Burger())

执行结果 :

吃普通食物
吃快餐
吃汉堡

以上是关于Kotlin泛型 ③ ( 泛型 out 协变 | 泛型 in 逆变 | 泛型 invariant 不变 | 泛型逆变协变代码示例 | 使用 reified 关键字检查泛型参数类型 )的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin中接口抽象类泛型out(协变)in(逆变)reified关键字的详解

对比Java学Kotlin泛型

对比Java学Kotlin泛型

对比Java学Kotlin泛型

对比Java学Kotlin泛型

对比Java学Kotlin泛型