为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Cats萌萌 2021-12-10 10:48:21
我试图解决一个我无法理解部分答案的问题。以下是课程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选项是正确的?我明白以下几点:public static <T> List<? extends T> backwards(List<T> input) 意味着返回类型应该是List的T或 的子类T。public static <T> List<? super T> backwards(List<T> input)意味着返回类型应该是List的T或超类T。有人可以帮我理解吗?
查看完整描述

3 回答

?
潇潇雨雨

TA贡献1833条经验 获得超4个赞

它们中的每一个都存在差异,我将解释其中的大部分。让我们从我们的例子开始。我使用这个类层次结构:


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 视为 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)   

如果您发送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)

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


我希望这对你有帮助。

如果有什么问题,任何评论都表示赞赏。


查看完整回答
反对 回复 2021-12-10
?
慕神8447489

TA贡献1780条经验 获得超1个赞

选项D和E是有效的,因为泛型类型之间存在超子类型关系,允许您定义方法可以接受或返回的更大的类型集。


因此,以下是有效的 (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本身)。


所以,你的理解是对的。


查看完整回答
反对 回复 2021-12-10
?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

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


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

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

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

//img1.sycdn.imooc.com//61b2c02900017f4d03350197.jpg

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


查看完整回答
反对 回复 2021-12-10
  • 3 回答
  • 0 关注
  • 251 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号