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

ski*_*kip 4 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

问题问哪个可以插入到// 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)
Run Code Online (Sandbox Code Playgroud)

我知道 A 和 B 是正确的,至于for (T t : input)工作中的元素input应该是 的类型T或子类型T

但我无法理解为什么D并且E选项是正确的?

我了解以下内容:

  1. public static <T> List<? extends T> backwards(List<T> input) 意味着返回类型应该是 的ListT的子类T
  2. public static <T> List<? super T> backwards(List<T> input)意味着返回类型应该是Listof 的T或其超类T

有人可以帮我理解吗?

Ami*_*min 5

它们之间各有不同,我将解释其中的大部分。让我们从我们的例子开始。我使用这个类层次结构:

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<>();
Run Code Online (Sandbox Code Playgroud)

现在从第一个开始:

A. public static <T> List<T> backwards(List<T> input)
Run Code Online (Sandbox Code Playgroud)

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

B. public static <T> List<T> backwards(List<? extends T> input)
Run Code Online (Sandbox Code Playgroud)

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

C. public static <T> List<T> backwards(List<? super T> input) 
Run Code Online (Sandbox Code Playgroud)

您可以发送listApple,然后在方法体中您可以添加任何扩展Apple,因为编译器将 T 视为并且在T 的超级Apple列表中,您可以添加任何扩展。 但是这一次,您无法读取任何内容,因为您不知道它的类型,除非您将其获取为. (这是“?超级T”的列表) Apple
Object

正如您在这里看到的,两者之间有区别吗?超级?延长。其中一个为您提供写访问权限,其他为您提供读访问权限。这就是通配符的真正用途。

D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)   
Run Code Online (Sandbox Code Playgroud)

如果您发送listApple,然后您返回List<? extends Apple>,但您可以将其分配给任何listFoodlistApple或,listRedApple因为List<? extends Apple>可能包含AppleRedApple或其他东西,我们不能将其分配给任何List<T>,因为然后我们可以添加T到该列表,也许T? extends T不一样。这对于 和 都是相同DE。您可以将其分配给List<? extends Apple>for 'E 和List<? super Apple>forD并将它们发送到需要它们作为参数的方法。

F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)
Run Code Online (Sandbox Code Playgroud)

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

我希望这对你有帮助。
如果有什么问题,任何评论都值得赞赏。