美文网首页
Android基础必备之架构设计核心知识点:泛型总结

Android基础必备之架构设计核心知识点:泛型总结

作者: 像程序那样去思考 | 来源:发表于2023-04-25 21:46 被阅读0次

    一、泛型类


    定义一个 泛型类 ,

    将 泛型参数 T 放在 尖括号 <T> 中 , 该泛型参数放在 类名后 , 主构造函数之前 , 该泛型参数 T 是 类型占位符 ,

    在 该泛型类类中 可以使用 类型占位符 T 作为一个类 使用 ,

    • 可以 定义 T 类型成员属性
    • 主构造函数中可以接收 T 类型的实例对象作为参数 ,
    • T 类型可以 作为函数的返回值 ;

    通常情况下 , 泛型参数 都使用 T 表示 , 使用其它字母 或者 字符串 都可以表示 泛型参数 , 但是 约定俗成 都使用 T 来表示泛型 ;

    代码示例 : 下面的代码中 , 声明了 Student 泛型类 , 该泛型类 接收 T 类型的泛型参数 , 在主构造函数中接收 T 类型的参数 , 在该泛型类中声明了 T 类型的成员属性 ;

    class Student<T>(_item: T) {
        var item: T = _item
        fun log(){
            println("item : $item")
        }
    }
    
    fun main() {
        var student: Student<String> = Student("Tom")
        student.log()
    
        var student2: Student<Int> = Student(18)
        student2.log()
    }
    

    执行结果 :

    item : Tom
    item : 18
    

    二、泛型参数


    通常情况下 , 泛型参数 都使用 T 表示 , 使用其它字母 或者 字符串 都可以表示 泛型参数 , 但是 约定俗成 都使用 T 来表示泛型 ;

    在下面的代码中 , 使用 M 作为 泛型参数 ;

    代码示例 :

    class Student<M>(_item: M) {
        var item: M = _item
        fun log(){
            println("item : $item")
        }
    }
    
    fun main() {
        var student: Student<String> = Student("Tom")
        student.log()
    
        var student2: Student<Int> = Student(18)
        student2.log()
    }
    

    执行结果 :

    item : Tom
    item : 18
    

    三、泛型函数


    函数 的 参数 或 返回值 类型为 泛型类型 , 则该函数称为 泛型函数 ;

    代码示例 : 该代码中 , 泛型函数 logT 的 参数 和 返回值 都是 T 泛型参数 类型 ;

    class Student<T>(_item: T) {
        var item: T = _item
    
        // 泛型函数 参数 和 返回值 都是 T 类型
        fun logT(t: T): T{
            println("item : $item")
            return item
        }
    }
    
    fun main() {
        var student: Student<String> = Student("Tom")
        println(student.logT(student.item))
    
        var student2: Student<Int> = Student(18)
        println(student2.logT(student2.item))
    }
    

    执行结果 :

    item : Tom
    Tom
    item : 18
    18
    

    复制

    四、多泛型参数


    泛型函数 中 如果涉及到 匿名函数 参数 , 匿名函数 的 参数返回值 都是泛型 的话 , 在该泛型函数 中可能需要使用多个泛型 , 使用不同的字母表示不同的泛型 ;

    如果函数中 引入了新的泛型类型 , 需要在 fun 关键字 和 函数名 之间 , 使用 尖括号 <T> 注明 ;

    代码示例 : 在本代码中 , logT 函数涉及到了两个泛型参数 , 传入的 匿名函数 参数类型为 (T) -> R , 需要两个泛型参数来表示其类型 ; T 类型在泛型类中注明 , 可以在该泛型类 Student 中随意使用 , 但是 泛型参数 R 是首次在该函数中使用 , 因此需要在该函数的 fun 关键字 和 函数名 之间 , 使用 尖括号 <R> 注明 新的泛型参数 ;

    class Student<T>(_item: T) {
        var item: T = _item
    
        fun <R> logT(action: (T) -> R): R{
            return action(item)
        }
    }
    
    fun main() {
        var student: Student<String> = Student("Tom")
        println(student.logT {3.14})
    
        var student2: Student<Int> = Student(18)
        println(student2.logT {true})
    }
    

    执行结果 :

    • student 实例对象中 , 泛型 T 的实际类型是 String 类型 , 在 logT 中 泛型 R 的类型是 Double 类型 ;
    • student2 实例对象中 , 泛型 T 的实际类型是 Int 类型 , 在 logT 中 泛型 R 的类型是 Boolean 类型 ;
    3.14
    true
    

    五、泛型类型约束


    在 泛型类 , 泛型函数 中 , 使用泛型前 , 需要声明 泛型参数 :

    • 泛型类 泛型参数 声明 : 如果类中 引入了新的泛型类型 , 需要在 class 关键字 和 主构造函数 之前 , 使用 尖括号 <T> 注明 ;
    class Student<T>(_item: T) {
    }
    
    • 泛型函数 泛型参数 声明 : 如果函数中 引入了新的泛型类型 , 需要在 fun 关键字 和 函数名 之前 , 使用 尖括号 <T> 注明 ;
        fun <R> logT(action: (T) -> R): R{
            return action(item)
        }
    

    在 尖括号 <> 中声明 泛型参数 时 , 可以指定 泛型类型 的 约束 , 如 泛型类型 必须是某个类型的子类 ;

    在下面的代码中 , Soldier 类的泛型 进行了约束 , 必须是 Weapon 类的子类类型 ;

    class Soldier<T : Weapon>(_item: T) {}
    

    Weapon 类是父类 , 其有一个 子类 AK47 类 ;

    在创建 Soldier 实例对象时 , 传入 子类 AK47 类的实例对象 , 调用其重写父类的 fire 函数 ;

    代码示例 :

    class Soldier<T : Weapon>(_item: T) {
        var item: T = _item
    
        fun fight(){
            item.fire()
        }
    }
    
    open class Weapon(var name: String){
        open fun fire(){
            println("weapon fire!")
        }
    }
    class AK47: Weapon("AK47"){
        override fun fire(){
            super.fire()
            println("fire in the hole !")
        }
    }
    
    fun main() {
        var soldier: Soldier<AK47> = Soldier(AK47())
        soldier.fight()
    }
    

    执行结果 :

    weapon fire!
    fire in the hole !
    

    六、可变参数 vararg 关键字与泛型结合使用


    如果 泛型类型 T 的参数 是 vararg 可变参数 , 则在接收 可变参数 时 , 需要使用 Array<out T> 类型 的变量进行接收 ;

    参数为 vararg 可变参数 , 那么可以传入多个 指定类型的 实例对象 ;

    在下面的代码中 , 声明了 泛型参数 T , T 类型不必须是 Weapon 类的子类类型 ;

    在 Soldier 的主构造函数中 , 传入了 泛型 T 类型的 可变参数 对象 ;

    如果要使用 成员属性 接收该 泛型 T 类型的 可变参数 对象 , 则必须 使用 Array<out T> 类型对象进行接收 ;

    代码示例 :

    class Soldier<T : Weapon>(vararg _items: T) {
        var items: Array<out T> = _items
    
        fun fight(){
            for (item in items) {
                item.fire()
            }
        }
    }
    
    open class Weapon(var name: String){
        open fun fire(){
            println("weapon fire !")
        }
    }
    class AK47: Weapon("AK47"){
        override fun fire(){
            super.fire()
            println("AK47 fire fire !")
        }
    }
    class T72: Weapon("T72"){
        override fun fire(){
            super.fire()
            println("T72 fire fire !")
        }
    }
    
    fun main() {
        var soldier: Soldier<Weapon> = Soldier(AK47(), T72())
        soldier.fight()
    }
    

    执行结果 :

    weapon fire!
    AK47 fire fire !
    weapon fire!
    T72 fire fire !
    

    七、使用 [] 运算符获取指定可变参数对象


    如果想要 使用 [] 运算符获取指定可变参数对象 , 就需要 重写 该类 的 get 函数 进行 运算符重载 ;

    image.png

    如果想要通过 Soldier 实例对象[] 运算符获取其 items: Array<out T> 中的某个元素 , 则需要重写该类的 get 方法 ;

        var items: Array<out T> = _items
    
        operator fun get(index: Int): T? {
            return items[index]
        }
    

    之后使用 soldier[0] 即可获取 items 可变参数中的实例对象 ;

        var soldier: Soldier<Weapon> = Soldier(AK47(), T72())
        soldier[0]?.fire()
        soldier[1]?.fire()
    

    代码示例 :

    class Soldier<T : Weapon>(vararg _items: T) {
        var items: Array<out T> = _items
    
        operator fun get(index: Int): T? {
            return items[index]
        }
    
        fun fight(){
            for (item in items) {
                item.fire()
            }
        }
    }
    
    open class Weapon(var name: String){
        open fun fire(){
            println("weapon fire !")
        }
    }
    class AK47: Weapon("AK47"){
        override fun fire(){
            super.fire()
            println("AK47 fire fire !")
        }
    }
    class T72: Weapon("T72"){
        override fun fire(){
            super.fire()
            println("T72 fire fire !")
        }
    }
    
    fun main() {
        var soldier: Soldier<Weapon> = Soldier(AK47(), T72())
        soldier[0]?.fire()
        soldier[1]?.fire()
    }
    

    执行结果 :

    weapon fire!
    AK47 fire fire !
    weapon fire!
    T72 fire fire !
    

    八、泛型 out 协变


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

    image.png

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

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

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

    九、泛型 in 逆变


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

    image.png

    在 泛型类 中 , 如果只将 泛型类型 作为 函数的参数 类型 , 则在 声明 泛型参数 类型 时 , 在 泛型参数 前 使用 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>();
        }
    }
    
    image.png
    • Kotlin 语言中 , 泛型参数是 子类 的 泛型类对象 , 可以赋值给 泛型参数 是父类 的变量 , 前提是泛型参数必须使用 out 关键字修饰 ;
      • 使用 in 关键字 , 可以使 父类泛型对象 赋值给 子类泛型对象 ;
      • 使用 out 关键字 , 可以使 子类泛型对象 赋值给 父类泛型对象 ;

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

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

    使用了 泛型 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())
    }
    

    执行结果 :

    吃普通食物
    吃快餐
    吃汉堡
    

    相关文章

      网友评论

          本文标题:Android基础必备之架构设计核心知识点:泛型总结

          本文链接:https://www.haomeiwen.com/subject/nommjdtx.html