Java泛型,数组和方法返回类型 - 协变,逆变和不变

本文讨论了Java编程语言中的协变和逆变概念,特别是在数组和泛型上下文中。Java数组是协变的,允许用子类型替换超类型,但可能导致ArrayStoreException。Java泛型则是不变的,防止类型安全问题。使用通配符?extends的泛型是协变的,仅支持读操作,而?super则使泛型逆变,允许写操作。此外,Java还支持协变方法返回类型,子类方法可以返回更具体的类型。
摘要由CSDN通过智能技术生成

首先,让我们通常理解一下子类型规则是什么。

协变vs逆变vs双变vs不变

编程语言可能有支持以下子类型规则的特性:

    协变

        允许用超类型替换子类型。

    逆变

        允许用子类型替换超类型。

    双变

        同时是协变和逆变。

    不变

        不允许上述任何替换。

让我们看看Java支持哪些子类型规则:

Java数组是协变的

Java数组支持协变替换:

Integer[] integers = new Integer[10];
Number[] numbers = integers;

但是,上面的赋值是危险的,因为它可能最终导致ArrayStoreException:

public class CovariantArraysExample {

  public static void main(String[] args) {
      Integer[] integers = new Integer[10];
      Number[] numbers = integers;
      numbers[0] = new Double(25);
  }
}

输出

Caused by: java.lang.ArrayStoreException: java.lang.Double
	at com.logicbig.example.CovariantArraysExample.main(CovariantArraysExample.java:8)
	... 6 more

Java泛型是不变的

Java泛型具有特定类型T,不支持协变或逆变替换。这是为了避免像我们在上面的数组案例中看到的那样出现ArrayStoreException的情况。

public class InvariantGenericsExample {

  public static void main(String[] args) {
      List<Integer> integers = new ArrayList<>();
      List<Number> numbers = new ArrayList<>();
      numbers = integers;
  }
}

输出

Compilation errors:
InvariantGenericsExample.java:[11,19] incompatible types: java.util.List<java.lang.Integer> cannot be converted to java.util.List<java.lang.Number>
1 error

带有通配符'?extends'的Java泛型是协变的:

public class CovariantGenericsExample {

  public static void main(String[] args) {
      List<Integer> integers = new ArrayList<>();
      integers.add(1);

      List<? extends Number> numbers = integers;
      System.out.println(numbers);
  }
}

输出

[1] 

在赋值给List<? extends Number>后,我们不能向List中添加Number的任何子类型。例如,这样的尝试将导致编译错误:

public class CovariantGenericsExample2 {

  public static void main(String[] args) {
      List<Integer> integers = new ArrayList<>();
      integers.add(1);

      List<? extends Number> numbers = integers;
      Double d = new Double(23);
      numbers.add(d);
  }
}

输出

Compilation errors:
CovariantGenericsExample2.java:[14,16] no suitable method found for add(java.lang.Double)
    method java.util.Collection.add(capture#1 of ? extends java.lang.Number) is not applicable
      (argument mismatch; java.lang.Double cannot be converted to capture#1 of ? extends java.lang.Number)
    method java.util.List.add(capture#1 of ? extends java.lang.Number) is not applicable
      (argument mismatch; java.lang.Double cannot be converted to capture#1 of ? extends java.lang.Number)
1 error 

原因是我们不能保证列表最初是为哪种类型的元素构造的。'?extends Number'表示任何扩展Number的类型,但编译器永远不知道是什么类型。我们只能向此类集合引用添加null,或者从中删除项目,但我们不能向其添加新元素。

带有通配符'?super'的Java泛型是逆变的:

Java泛型允许用'? super'通配符将超类型替换为子类型:

public class ContravariantGenericsExample {

    public static void main(String[] args) {
        List<A> aList = new ArrayList<>();
        aList.add(new A());

        List<? super B> bList = aList;
        bList.add(new B());

        System.out.println(bList);
    }

    private static class A {
    }

    private static class B extends A {
    }
}

输出

[com.logicbig.example.ContravariantGenericsExample$A@436e27a6, com.logicbig.example.ContravariantGenericsExample$B@69c3928e]

请注意,在上述情况下,我们在赋值后能够添加类型B的元素。这是因为,编译器知道bList是B的任何超类型的列表,而B(或B的子类型)肯定可以始终分配给B的超类型。

另一方面,从'? super B',编译器将永远不知道B的哪个超类型,这意味着我们不能向bList添加类型A或A的其他子类型的任何元素。

Java中的协变方法返回类型

Java允许子类覆盖可以返回更特定类型的方法。换句话说,子类方法的返回类型必须能够替换超类方法的返回类型。

方法的协变返回类型是当方法在子类或子接口中被覆盖时,可以被“更窄”的类型(子类型)替换的类型。

换句话说,被覆盖的方法可以有更具体的返回类型。只要新的返回类型可以赋值给我们正在覆盖的方法的返回类型,它就可以被使用。

Java自5.0版本以来就支持这个特性。

public class CovariantReturnExample {

    interface SuperType {
    }

    interface SubType extends SuperType {
    }

    interface A {
        SuperType getType ();
    }

    interface B extends A {
        SubType getType ();
    }
}

注意:协变返回可以在扩展类或扩展接口时使用。在上述示例中,SuperType-SubType 和/或 A-B 可以是类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值