Article Directory
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;
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;
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>();
}
}
- 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)
The use of generic out covariance and generic in contravariance greatly improves the scalability of the program;
In the code below, FoodFactory
is Producer<Food>
the subclass, and the type exactly matches;
// FoodFactory 是 Producer<Food> 子类 , 类型正好匹配
val producer: Producer<Food> = FoodFactory();
FastFoodFactory
is Producer<FastFood>
a subclass, Producer
the generic parameter FastFood
of is Food
a 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 :
吃普通食物
吃快餐
吃汉堡