在学习使用泛型编程时,更令人困惑的方面之一是确定何时使用上界通配符以及何时使用下界通配符。此页面提供了一些在设计代码时要遵循的准则。
    为了便于讨论,将变量视为提供以下两个功能之一将很有帮助:
    “输入”变量“输入”变量将数据提供给代码。想象一个具有两个参数的copy方法:copy(src,dest)。参数src提供将要复制的数据,所以它是“输入”参数。
    “输出”变量“输出”变量保存要在其他地方使用的数据。在copy示例,copy(src,dest)中,dest参数接受数据,因此它是“输出”参数。
    当然,某些变量既用于“输入”又用于“输出”目的-准则中也解决了这种情况。
    在决定是否使用通配符以及哪种类型的通配符时,可以使用“输入”和“输出”原理。以下列表提供了要遵循的准则:


    通配符准则:

    • 使用extends关键字,使用上界通配符定义“ 输入”变量。
    • 使用super关键字使用下界通配符定义“输出”变量。
    • 如果可以使用Object类中定义的方法访问“ 输入”变量,请使用无界通配符。
    • 如果代码需要同时使用“输入”和“输出”变量来访问变量,则不要使用通配符。

    这些准则不适用于方法的返回类型。应该避免使用通配符作为返回类型,因为这会迫使程序员使用代码来处理通配符。
    由 List<? extends …>定义的列表可以非正式地认为是只读的,但这不是严格的保证。假设您具有以下两个类:

    1. class NaturalNumber {
    2. private int i;
    3. public NaturalNumber(int i) { this.i = i; }
    4. // ...
    5. }
    6. class EvenNumber extends NaturalNumber {
    7. public EvenNumber(int i) { super(i); }
    8. // ...
    9. }

    考虑以下代码:

    1. List<EvenNumber> le = new ArrayList<>();
    2. List<? extends NaturalNumber> ln = le;
    3. ln.add(new NaturalNumber(35)); // compile-time error

    因为List 是List<? extends NaturalNumber>的子类型,您可以将le分配给ln。但是您不能使用ln将自然数添加到偶数列表中。列表上的以下操作是可能的:

    • 您可以添加null。
    • 您可以调用clear。
    • 您可以获取迭代器并调用remove。
    • 您可以捕获通配符并写入从列表中读取的元素。

    您可以看到由List<? extends NaturalNumber> 定义的列表,从严格意义上来说,不是只读的,但您可能会这样想,因为您无法存储新元素或更改列表中的现有元素。