繁体   English   中英

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

[英]Upper-Bounded and Lower-Bounded Wildcards in return type of Java 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// 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) public static <T> List<? extends T> backwards(List<T> input)意味着返回类型应该是一个ListT或子类T
  2. public static <T> List<? super T> backwards(List<T> input) public static <T> List<? super T> backwards(List<T> input)意味着返回类型应该是一个ListT的或超类T

有人可以帮我理解吗?

每个人之间都存在差异,我将解释他们中的大多数。 让我们从我们的例子开始。 我使用这个类层次结构:

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“?extend Apple”所以在方法体java中将T识别为Apple。 然后如果你使用可以在listRedApple中添加作为参数发送的元素,你可以在listRedApple中添加Apple ,这不是真的! 所以编译器避免它并给出编译错误。 在B中,您只能读取元素 (并将其作为T),但您无法添加任何内容。

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

您可以发送listApple ,然后在方法体中,您可以添加任何扩展Apple因为编译器将T视为Apple并且在任何超级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> List<? extends Apple>但您可以将其分配给listFoodlistApplelistRedApple任何一个,因为List<? extends Apple> List<? extends Apple>可能包含AppleRedApple或其他东西,我们不能将它分配给任何List<T>因为那样我们可以将T添加到该列表中,也许T? extends T ? extends T不一样。 DE都是一样的。 你可以将它分配给List<? extends Apple> List<? extends Apple>为'E和List<? super Apple> List<? super Apple>用于D并将它们发送到需要它们作为参数的方法。

F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)

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

我希望这对你有帮助。
如果出现问题,任何评论都表示赞赏。

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

因此,以下内容有效(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 ? 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 ? super Long表示(家庭是的超类型类型Long和类型的Long本身)。

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

进一步阅读

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

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

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

在此输入图像描述

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM