[Kotlin] Generics ③ ( Generic out covariant | Generic in invariant | Generic invariant invariant | Generic invariant covariant code example | Use the reified keyword to check generic parameter types )


Summary of this chapter: The use of generic out covariance and generic in contravariance greatly improves the scalability of the program;

  • Generic in contravariance: use the in keyword, you can assign the generic object of the parent class to the generic object of the subclass;
  • Generic out covariance: use the out keyword to assign a subclass generic object to a parent generic object;




1. Generic out covariance



Using the out keyword, the subclass generic object can be assigned to the parent class generic object;
insert image description here

In the generic class , if only the generic type is used as the return value type of the function , then when declaring generic parameter type , use the , and the generic class is also called the production class (production Interface) , used to produce generic objects specified by generic classes;


Code example: In the following interface, the generic type is only used as the return value;

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




2. Generic in inversion



Using the in keyword, the parent class generic object can be assigned to the subclass generic object;
insert image description here

In the generic class , if only the generic type is used as the parameter type of the function , then when declaring generic parameter type , use the , and the generic class is also called the consumer class (consumer interface ) , used to consume the generic object specified by the generic class;


Code example: In the interface below, generic types are only used as parameters;

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




3. The generic invariant remains unchanged



In a generic class , if

  • Both use the generic type as the parameter type of the function ,
  • And use the generic type as the return value type of the function ,

When declaring generic parameter type , neither the in keyword nor the out keyword is used;


Code example: In the interface below, the generic type is used both as a return value and as a parameter;

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




4. Generic contravariant covariant code example



Generic parameters in generic classes have subclasses and parent classes,

  • In the Java language, generic parameters are generic class objects of subclasses, and cannot be assigned to Generic parameters are variables of the parent class;
    • Generic object assignment in Java, there is no inheritance relationship, whatever type is what type, the type must be strictly the same;
import java.util.ArrayList;

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

insert image description here

  • In the Kotlin language, a generic parameter is a generic class object of a subclass, which can be assigned to a variable of the parent class. The premise is that the generic parameter must be modified with the out keyword;
    • Using the in keyword, the parent class generic object can be assigned to the subclass generic object;
    • Using the out keyword, the subclass generic object can be assigned to the parent class generic object;

In the figure below, the scope of the parent class is larger than the scope of the child class ,

  • If the in keyword is used , the generic object of the parent class with a large scope is assigned to the generic object of the subclass with a small scope , (or an error will be reported)
  • If the out keyword is used , the subclass generic object with a small range is assigned to the parent class generic object with a large range; (otherwise an error will be reported)

insert image description here

The use of generic out covariance and generic in contravariance greatly improves the scalability of the program;


In the code below, FoodFactoryis Producer<Food>the subclass, and the type exactly matches;

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

FastFoodFactoryis Producer<FastFood>a subclass, Producerthe generic parameter FastFoodof is Fooda subclass of , in Kotlin, the Producer<FastFood>type can be assigned to the Producer<Food>type , but this usage is not available in Java;

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

Code example:

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())
}

Results of the :

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




5. Use the reified keyword to check the generic parameter type



The generic parameter type T will be type -erased at runtime , so the specific type of the generic parameter is not known at runtime ,

With the help of the reified keyword , the specific type of the runtime generic parameter can be checked;


In Java, the runtime does not know the specific type of the generic parameter ; in Kotlin, the generic parameter type can be checked through the reified keyword;


In Java, if you want to know the specific type of generic parameters, you can’t do it through conventional methods, but you can do it through reflection ;

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

To use the need to add the reified keyword before the generic and the function must also be modified with the inline keyword;

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

In the inline function that uses the reified keyword to modify the generic type, you can use is to determine the specific type of the generic parameter;


Code example:

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())
}

Results of the :

吃普通食物
吃快餐
吃汉堡

Guess you like

Origin blog.csdn.net/han1202012/article/details/128747664