java的协变性、逆变性、不变性

    先看看官方的解释:协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。协变和逆变是指宽类型和窄类型在某种情况下(如参数、泛 型、返回值)替换或交换的特性。

    简单地说A和B是类型,f表示类型转换,≤表示子类型关系:

    协变:你可以用一个子类对象去替换相应的一个父类对象。如果A≤B 则f(A) ≤ f(B) 那么 f是协变的 。如果能在泛型接口或者委托中保证,类型参数只能外部取出而不允许外部传入。那么就可以避免将类型参数作为参数传入方法的情况。可以通过在类型参数前面加out来解决这个问题。

    逆变:你可以用一个父类对象去替换相应的一个父类对象。如果A≤B 则f(B) ≤ f(A) 那么 f 是逆变的。如果能在泛型接口或者委托中保证,类型参数只能作为参数从外部传入而不允许将其取出。那么就不存在将类型参数作为返回值返回的情况了。可以通过在类型参数前面加in来解决这个问题。

    不变:对于不支持协变和逆变的情况称为不变性。上面两种都不成立,那么f是无关的。

    来实际说明一下协变性和不可性在java中的具体体现。

    一、java的数组就具有协变性

        数组的协变性是指如果类Base是类Sub的基类,那么Base[]就是Sub[]的基类。数组是协变的导致了很多问题的出现,Object[]类型的引用可以指向一个String[]类型的对象。但是运行的时候是会报出如下异常的:Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer 试图将错误类型的对象存储到一个对象数组时抛出的异常。这样导致数组的类型并不安全。数组则是在运行时做类型检查,因为能够记得数组内部元素的具体类型。

   二、java的泛型就具有不变性

        泛型是不可变的,List<Base>不会是List<Sub>的基类,更不会是它的子类。为了遵守泛型类型安全原则,所以在泛型“编译时进行类型检查”特性决定了其不可协变。因为在泛型进行类型擦除,所以只能选择在编译期进行类型检查,因为一旦运行起来这个类型就会被擦除掉,如果类型不一致编译就不会通过。为了让泛型更灵活也可以具备协变性,就可以使用通配符来模拟协变。

猜你喜欢

转载自blog.csdn.net/ZytheMoon/article/details/85004086