Java通用方法的返回类型的上限和下限通配符

时间:2018-10-26 13:45:28

标签: java generics wildcard generic-method

我试图解决一个我无法理解部分答案的问题。

以下是课程BackLister

public class BackLister {
    // INSERT HERE
    {
        List<T> output = new LinkedList<T>();
        for (T t : input)
            output.add(0, t);
        return output;
    }
}

问题询问可以在// INSERT HERE类的BackLister插入哪些内容以进行编译和运行而不会出错?

以下是选项:

A. public static <T> List<T> backwards(List<T> input)
B. public static <T> List<T> backwards(List<? extends T> input)
C. public static <T> List<T> backwards(List<? super T> input)
D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)
F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)

我了解A和B是正确的,因为for (T t : input)要使input中的元素起作用,应该是T类型或T的子类型。

但是我不明白为什么DE选项正确吗?

我了解以下内容:

  1. public static <T> List<? extends T> backwards(List<T> input)     表示返回类型应为List的{​​{1}}或子类     T中的。
  2. T表示返回类型应为public static <T> List<? super T> backwards(List<T> input)的{​​{1}}或 List的超类。

有人可以帮我理解吗?

3 个答案:

答案 0 :(得分:2)

它们之间有区别,我将解释它们中的大多数。让我们从我们的例子开始。我使用该类层次结构:

class Food {}
class Apple extends Food {}
class Orange extends Food {}
class RedApple extends Apple {}

List<Food> listFood = new ArrayList<>();
List<Apple> listApple = new ArrayList<>();
List<Orange> listOrange = new ArrayList<>();
List<RedApple> listRedApple = new ArrayList<>();

现在从第一个开始:

A. public static <T> List<T> backwards(List<T> input)

此方法将仅接受List<T>并返回List<T>,而您不能发送listApple并返回listRedApple。 (但是,您的返回列表可以包含RedApple,因为它扩展了Apple,但是列表的类型必须为List<Apple>,不能有其他内容)

B. public static <T> List<T> backwards(List<? extends T> input)

您可以发送listRedApple并返回listApple,但是您知道listRedApple“?延伸Apple” ,因此在方法主体中Java将T识别为Apple 。然后,如果使用可以在作为参数发送的listRedApple中添加元素,则可以在Apple中添加listRedApple,这是不正确的!!!因此,编译器避免使用它并给出编译错误。在B中,您只能读取元素(并将其获取为T),但不能对其添加任何内容。

C. public static <T> List<T> backwards(List<? super T> input) 

您可以发送listApple,然后在方法主体中添加任何扩展Apple,因为编译器将T视为Apple,并在列表中的任何内容都超过T ,您可以添加扩展Apple的所有内容。
但是这一次,您无法读取任何内容,因为您不知道它的类型,除非得到它的名称为Object。 (这是“?super T” 的列表)

如您所见,之间有区别吗?超级?扩展。其中一个给您写访问权限,另一个给您读访问权限。这是通配符的实际用法。

D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)   

如果发送listApple,则返回List<? extends Apple>,但是可以将其分配给listFoodlistApplelistRedApple中的任何一个,因为List<? extends Apple>可能包含AppleRedApple或其他内容,我们无法将其分配给任何List<T>,因为这样我们就可以将T添加到该列表中,并且可能添加T? extends T不同。 DE都是一样的。您可以将其分配给'E的List<? extends Apple>List<? super Apple>的{​​{1}},然后将它们发送给需要它们作为参数的方法。

D

给出编译错误,因为不能像这样使用通配符。

希望对您有所帮助。
如果有什么问题,我们欢迎您提出任何意见。

答案 1 :(得分:2)

选项DE是有效的,因为通用类型之间存在 super-subtype 关系,这些关系允许您定义该方法可以接受的更大类型的集合或返回。

因此,以下有效(D):

public static <T> List<? extends T> backwards(List<T> input) {
    return List.of();
}

// there exist a super-subtype relationships among List<? extends Number> and List<Long>
List<? extends Number> list = backwards(List.<Long>of(1L, 2L));

因为类型Long是通配符? extends Number表示的类型族的成员(类型族是Number的子类型,而类型Number本身)。

下一个代码段也有效(E):

public static <T> List<? super T> backwards(List<T> input) {
    return List.of();
}

List<? super Long> ints = backwards(List.<Long>of(1L, 2L));

因为类型Long是通配符? super Long表示的类型族的成员(类型族是Long的超类型,而类型Long本身)。

所以,您的理解是正确的。

Further reading

答案 2 :(得分:1)

下图描述了泛型中的子类型关系:

List<T> is a subtype of List<? super T>
Also List<T> is a subtype of List<? extends T>

这就是为什么选项D和E正确的原因。

enter image description here

您可以参考页面:https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html