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

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


以下是课程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。


但我不明白为什么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)意味着返回类型应该是ListT或超类T

有人可以帮我理解吗?


Cats萌萌
浏览 286回答 3
3回答

潇潇雨雨

它们中的每一个都存在差异,我将解释其中的大部分。让我们从我们的例子开始。我使用这个类层次结构: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)&nbsp;您可以发送listApple,然后在方法主体中添加任何扩展,Apple因为编译器将 T 视为 TApple的任何列表,并且在T的任何超列表中,您可以添加任何扩展Apple。但是这一次,您无法读取任何内容,因为您不知道它的类型,除非您将其作为Object. (这是一个“?超级T”的列表)正如你在这里看到的,有区别吗?超级和?延伸。其中之一为您提供写访问权限,另一个为您提供读访问权限。这就是通配符的真正用途。D. public static <T> List<? extends T> backwards(List<T> input)E. public static <T> List<? super T> backwards(List<T> input)&nbsp; &nbsp;如果您发送listApple然后您返回,List<? extends Apple>但您可以将它分配给任何listFood或listApple或listRedApple因为List<? extends Apple>可能包含Apple或RedApple或其他东西,我们不能将它分配给任何List<T>因为然后我们可以添加T到该列表中,并且可能T和? extends T不一样。这对D和都是一样的E。您可以将其分配给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)给出编译错误,因为不能像这样使用通配符。我希望这对你有帮助。如果有什么问题,任何评论都表示赞赏。

慕神8447489

选项D和E是有效的,因为泛型类型之间存在超子类型关系,允许您定义方法可以接受或返回的更大的类型集。因此,以下是有效的 (D):public static <T> List<? extends T> backwards(List<T> input) {&nbsp; &nbsp; 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) {&nbsp; &nbsp; return List.of();}List<? super Long> ints = backwards(List.<Long>of(1L, 2L));因为该类型Long是通配符所? super Long表示的类型家族的成员(作为超类型的类型家族Long和类型Long本身)。所以,你的理解是对的。

幕布斯6054654

下图描述了泛型中的子类型关系:List<T> is a subtype of List<? super T>Also List<T> is a subtype of List<? extends T>这就是为什么选项 D 和 E 是正确的。您可以参考该页面:https&nbsp;:&nbsp;//docs.oracle.com/javase/tutorial/java/generics/subtyping.html
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java