我试图解决一个我无法理解部分答案的问题。
以下是课程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
的{{1}}或子类
T
中的。 T
表示返回类型应为public static <T> List<? super T> backwards(List<T> input)
的{{1}}或
List
的超类。有人可以帮我理解吗?
答案 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>
,但是可以将其分配给listFood
或listApple
或listRedApple
中的任何一个,因为List<? extends Apple>
可能包含Apple
或RedApple
或其他内容,我们无法将其分配给任何List<T>
,因为这样我们就可以将T
添加到该列表中,并且可能添加T
与? extends T
不同。 D
和E
都是一样的。您可以将其分配给'E的List<? extends Apple>
和List<? super Apple>
的{{1}},然后将它们发送给需要它们作为参数的方法。
D
给出编译错误,因为不能像这样使用通配符。
希望对您有所帮助。
如果有什么问题,我们欢迎您提出任何意见。
答案 1 :(得分:2)
选项D
和E
是有效的,因为通用类型之间存在 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
本身)。
所以,您的理解是正确的。
答案 2 :(得分:1)
下图描述了泛型中的子类型关系:
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