Kotlin 泛型 | 01. 基础
一、泛型1.1 含义参数化类型用尖括号这种方式表示如T、E、?等。比如方法的参数一般指定具体类型如果把参数的类型也参数化那这就是泛型本尊了。interfaceListout E : CollectionE {override fun contains(element:UnsafeVarianceE): Boolean}总的来说泛型本质就是参数化类型这种类型可以用在类、接口和方法的创建中分别称为泛型类、泛型接口、泛型方法。1.2 好处让类型更加安全。编译时类型检查。将错误暴露在编译期不用等到运行时防止在运行时出现 ClassCastException。运行时自动类型转换。不用类型强制转换的烦琐操作。更加语义化比如ListString清楚知道存储的是 String 对象和能写出更加通用化的代码引入泛型后并未增加代码的冗余性。1.3 类型约束泛型本身就有类型约束的作用那么这里的类型约束实际上指的是一种缩小范围的约束。目标理解3个问题——上界约束是什么类型是否为空多个条件约束怎么办1.3.1 上界约束最常见的约束就是与 Java 的 extends 关键字对应的上界约束如class FruitPlateT : Fruit(val t: T)。冒号之后指定的类型 Fruit 是上界只有 Fruit 的子类型可以替代 T。classDemo {classNoodles(weight: Double)// 面条openclassFruit(val weight: Double)// 水果classApple(weight: Double) : Fruit(weight)// 苹果classBanana(weight: Double) : Fruit(weight)// 香蕉classFruitPlateT : Fruit(val t: T)// 上界约束fun demo() {FruitPlate(Apple(1.6))FruitPlate(Banana(1.6))FruitPlate(Noodles(1.6))// 报错}}如果没有声明默认的上界是Any?。声明上界后也可以使用?手动指定可空。classDemo {openclassFruit(val weight: Double)// 水果classFruitPlateT : Fruit?(val t: T)// 上界约束fun demo() {FruitPlate(null)// 正确}}在尖括号中只能指定一个上界。 如果同一类型参数需要多个上界那么需要一个单独的where子句如fun T cut(t: T) where T : Fruit, T : Ground {}classDemo {interfaceGround// 土地classNoodles(weight: Double)// 面条openclassFruit(val weight: Double)// 水果classBanana(weight: Double) : Fruit(weight)// 香蕉classWatermelon(weight: Double) : Fruit(weight), Ground// 西瓜长在土地的水果fun T cut(t: T) where T : Fruit, T : Ground {}fun demo() {cut(Watermelon(1.6))// 所传递的类型 T 必须同时满足 where 子句的所有条件cut(Noodles(1.6))// 报错cut(Banana(1.6))// 报错}}多个上界助记语where关键字逗号分隔开。二、泛型的背后类型擦除2.1 泛型与数组对比2.1.1 Java 中泛型与数组对比泛型Java 泛型是不变的Fruit 是 Apple 的父类ListFruit 不是 ListApple 的父类是类型擦除的可以看做伪泛型无法在程序运行时获取到一个对象的具体类型。保证类型安全。数组Java 数组是协变的Fruit 是 Apple 的父类Fruit[] 是 Apple[] 的父类在程序运行时可以获取自身的类型。保证类型安全。publicclassJavaDemo {Apple[] appleArray newApple[10];Fruit[] fruitArray appleArray;// 允许ListApple appleList newArrayList();ListFruit fruitList appleList;// 不允许publicstaticclassAppleimplementsFruit { }publicinterfaceFruit { }}2.1.2 Kotlin 中泛型与数组对比泛型kotlin 中的泛型机制和Java一样数组kotlin 数组支持泛型不再协变Java 数组不支持泛型支持协变classDemo {openclassFruit(val weight: Double)// 水果classApple(weight: Double) : Fruit(weight)// 苹果privateval appleArray arrayOfNullsApple(5)val anyArray: ArrayFruit? appleArray// 不允许}2.2 小思考1、Java 为什么使用类型擦除来实现泛型并且怎么满足泛型应该具有的特性类型检查、类型自动转换Tips向后兼容强制类型转换2、为什么 Java 中数组不支持泛型Tips不再保证类型安全。数组内的元素必须是“物化”的因为类型被擦除后虚拟机建立数组时不知道类型而虚拟机要求数组建立时必须明确类型和长度。反证法假如给数组加上泛型后将无法满足数组协变的原则因为在运行时无法知道数组的类型。3、类型擦除后Kotlin 如何在运行时知道泛型参数的类型其实并不是真的将全部的类型信息都擦除还是会将类型信息放在对应 class 的常量池中Tips匿名内部类、内联函数 reified