一文了解 Java/Kotlin 中的泛型

本文原始发表于 https://juejin.cn/post/7077811938247311390

阅读本文你将了解:

  • 什么是型变、协变、逆变和不型变
  • 在 Java 和 Kotlin 中如何实现以上型变
  • Java 和 Kotlin 中泛型的异同

在 Java/Kotlin 中,子类对象是可以赋值给一个父类类型的,但是父类对象不可以赋值给子类类型,例如:

// Dog 是 Animal 的子类
class Animal {
   }
class Dog: Animal() {
   }

val animal: Animal = dog // 把子类对象赋值给一个父类类型是可以的,dog 也是一种 Animal
val dog: Dog = animal // 把父类对象赋值给一个子类类型是不可以的,不是所有 animal 都是 dog

在引入泛型之后,情况变得更复杂:类型参数为子类的泛型类型不是类型参数为父类的泛型类型的子类,听起来很绕,看代码:

// dogs 不是 animals 的子类
val dogs: List<Dog>
val animals: List<Animal>
// dogs 和 animals 不具备任何继承关系,因此以下代码会编译报错
val animals: List<Animal> = dogs

类型参数: 泛型中尖括号中的参数称为类型参数,比如 List<String> 中的 String 就是类型参数,和普通参数不同,类型参数传递的是一个类型而不是对象

为了描述方便,以下把所有「类型参数为子类的泛型」简称为「子类泛型」,「类型参数为父类的泛型」简称为「父类泛型」

对于从 Java 转到 Kotlin 的开发者们来说,要了解泛型,最好先搞懂 Java 中的泛型,再来看 Kotlin 的泛型时会变得易如反掌。

Java 中的泛型

泛型的型变(variance)

  • 协变(Covariance):子类泛型是父类泛型的子类型,可以把子类泛型赋值给父类泛型
  • 逆变(Contravariance):父类泛型(可以看作)是子类泛型的子类型,可以把父类泛型赋值给子类泛型
  • 不型变(Invariant):子类泛型和父类泛型没有任何继关系,也不可以相互赋值

👆🏻说的是偏概念的描述,听起来特别绕,特别反人类,用代码来说人话就是(已知 DogAnimal 的子类):

  • 协变(Covariance):List<Dog>List<Animal> 的子类型,List<Dog> 类型的对象可以赋值给 List<Animal> 类型的变量
  • 逆变(Contravariance):List<Animal> (可以看作) 是 List<Dog> 的子类型,List<Animal> 类型的对象可以赋值给 List<Dog> 类型的变量
  • 不型变(Invariant):List<Animal>List<Dog> 不具备任何继关系,也不可以相互赋值

协变、逆变本来是数学中的概念,在 Java/Kotlin 中主要应用在泛型中。

不型变

Java 中泛型是不型变的,也就是上例子上 List<Dog> 不是 List<Animals> 的子类,因此 List<Dog> 不可以赋值给 List<Animals>值得注意的是,Java 中数组是协变的:

List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = dogs; // 编译报错

Dog[] dogs = new Dog[] { new Dog() };
Animal[] animals = dogs; // 编译正常
animals[0] = new Animals(); // 运行时异常

因此我们在 Java 中要优先使用泛型集合

协变

不型变性是为了保证类型安全,但带来的代价就是使得程序的灵活性降低。有时候我们希望把子类泛型对象作为实参传递给一个声明为父类泛型的形参,例如:

public int getAnimalsCount(List<Animal> animals) {
   
  return animals.size();
}

List<Dog> dogs = new ArrayList<Dog>();
int dogsCount = getAnimalsCount(dogs); // 由于 Java 泛型的不型变,这里会编译报错的

以上,把 dogs 传递给 getAnimalsCount 方法用于计算狗狗的数量,这是一个特别合理的需求,因为不型变性导致这类需求无法实现

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值