有人可以解释一下这两种方法之间的区别吗?它们一样吗?就他们解决的问题而言,他们看起来与我相同。如果它们相同,为什么需要?
?
方法#1,无界限
public static void printList(List<?> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
方法#2,无界限
public static <T> void printList(List<T> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
方法#1,有界
public static void printList(List<? extends Number> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
方法#2,有界:
public static <T extends Number> void printList(List<T> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
答案 0 :(得分:29)
它们是相同的,因为它们接受相同的参数类型。
但是,使用T
(或其他)识别类型可让您在其他位置引用该类型。
编辑:示例:
您的无界示例未充分利用参数化类型的功能。你有:
public static <T> void printList(List<T> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
这对于打印字符串表示的示例就足够了,但考虑到这一点(非常人为,并且没有错误处理):
public static <T> T getSecondItem (List<T> list) {
T item = list.get(1);
return item;
}
返回类型为T,允许您安全地执行此类操作,并使用编译时类型检查:
class MyClass {
public void myMethod () { }
}
void somewhere () {
List<MyClass> items = ...;
getSecondItem(items).myMethod();
}
命名类型还允许您在多个位置共享相同的类型约束,例如:
public <T> int compareLists (List<T> a, List<T> b) {
...
}
如果您没有为该类型命名,则无法指定a
和b
是相同列表类型的约束(您可以使用List<? extends T>
以获得更大的灵活性)。
您还问过“我为什么需要?
?”。真正的答案是:你没有。我想,这主要是为了美学。 Java致力于成为一种精确且无杂乱的语言。在许多情况下,您根本不关心您所指的类型。在这些情况下,您可以使用?
而不会使用未使用的类型参数声明来混淆代码。
答案 1 :(得分:5)
在您的示例中,完全没有任何区别。每个都会产生相同的输出。
泛型的最佳使用和解释要求您了解类型参数的语义以及有关参数角色的内容。这样做的原因是,在上面的第一个例子(无界外卡)中,语义是“未知类型的对象列表”和“将产生(不消耗)List<?>
个实例的参数”。
上述方法只生成每个List<?>
对象,并在每个对象上调用toString()
。保证所有对象都具有toString()
方法,因此没有必要为此目的了解对象的类型。这正是无界通配符是此方法参数的最佳选择的原因:要生成List<?>
个实例并在其上调用toString()
,不必了解对象的类型。
请注意,如果?
具有相同的语义(“未知类型的对象列表”)但目的不同(“List将使用未知类型的对象”),事情会发生非常非常根本的变化因此使用通配符参数可能是不可取的(或非常困难)(至少没有辅助方法来捕获对象的类型)。
通常不可能为所有情况断言一个通用参数表单。使用的最佳形式(通配符与具体;有界与无界;扩展与超级)取决于类型参数的语义以及方法中参数的作用。
答案 2 :(得分:4)
如上所述,?
表示通用代码不需要对类型的任何引用。正如Java Trails中所指出的,这可能是一个类,其中属性独立于任何类型,如集合的长度。
?
的另一个原因是,当通用代码只需要类Object提供的行为时,它为不太模糊的语法提供了语法。正如上面所指出的,参数类型<T>
会起作用,但T被定义然后从未使用的事实可能表明开发人员遗漏了一些东西。
因此,如果由于语义歧义导致<T>
被删除而且<?>
不可用,那么开发人员会想要编写<Object>
。但是,这不允许将通用代码用于任何类型,只允许使用Object类型。
因此,<?>
更精确。