Java中的Covariance, Contravariance, Invariant

0. Covariance and Contravariance, Invariant

三者的定义:

  • Covariance: accept subtypes

  • Contravariance: accept supertypes

  • Invariant:除上面2种情况

1. 数组

数组是covariant的

1.1 T[]可以保存其子类

Number[] nums = new Number[5];
nums[0] = new Integer(1); // Ok
nums[1] = new Double(2.0); // Ok

1.2 当S<=T时, S[]<=T[]

Integer[] intArr = new Integer[5];
Number[] numArr = intArr; // Ok

1.3 它的问题

以下的代码编译期没有问题,但是由于栈污染,在运行时会报错 ArrayStoreException

numArr[0] = 1.23; // Not ok

2. 范型

范型是invariant的,这样可以避免运行时的栈污染

由于范型在运行时由于类型擦除不知道具体类型,所以不能在运行时检测出栈污染,所以范型是invariant,运行时没有类型,没法进行子类,父类的比较

ArrayList intArrList = new ArrayList<>();
ArrayList numArrList = intArrList; // Not ok
ArrayList anotherIntArrList = intArrList; // Ok

3. 通配符

通配符让范型有能力支持covariance和contravariance,例如:

ArrayList intArrList = new ArrayList<>();
ArrayList numArrList = intArrList; // Ok
ArrayList inarr = intArrList;  // ok

ArrayList numArrList = intArrList; // Not Ok
ArrayList inarr = intArrList;  // ok

Java中表示类型的上界和下界

下界(Contravariant):

? super Integer

上界(Covariant):

? extends Integer

4. 副作用

4.1 Covariant types是read-only

由于Covariant可以保证我们读取的数据一定是一个什么类型(可以使用它的下界)

ArrayList可以保证读出来的肯定可以转成Number类型

但是写数据的时候不能保证,写入的是正确的类型,因为nums.add()不能保证类型

4.2 Contravariant types是write-only

ArrayList可以保证这个对象接受Integer对象,即我们可以写入Integer,但不能保证我们读出来的一定是个Integer

5. 应用

Producer extends, Consumer super

producer-like的对象可以产出T类型的对象,所以类型可以是 ,可以作为函数的返回参数

consumer-like的对象可以消费T类型的对象,所以类型可以是,可以作为函数的入参

Ref:

  1. https://www.freecodecamp.org/news/understanding-java-generic-types-covariance-and-contravariance-88f4c19763d2/
  2. https://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-are-java-generics-not-implicitly-po/2745301#2745301
  3. Covariance and contravariance (computer science) - Wikipedia

你可能感兴趣的:(Java中的Covariance, Contravariance, Invariant)