在Java tutorials中,有时会写出这样的内容:
Set<?> unknownSet = new HashSet<String>();
虽然我理解在类定义和方法中使用类型参数和通配符的好处,但我想知道以下内容:
答案 0 :(得分:3)
通配符仅在方法参数声明中非常有用,因为它们会增加可接受参数类型的范围,例如:
void original(List<Number> list) { /* ... */ }
void withUpperWildcard(List<? extends Number> list) { /* ... */ }
void withLowerWildcard(List<? super Number> list) { /* ... */ }
original(new ArrayList<Number>()); // OK.
original(new ArrayList<Integer>()); // Compiler-error.
original(new ArrayList<Object>()); // Compiler-error.
withUpperWildcard(new ArrayList<Number>()); // OK.
withUpperWildcard(new ArrayList<Integer>()); // OK.
withLowerWildcard(new ArrayList<Number>()); // OK.
withLowerWildcard(new ArrayList<Object>()); // OK.
返回类型中的通配符会让您的类的用户感到生活困难(或者更糟糕),因为您必须传播它们,或者做一些明确的工作以使它们消失,例如:
List<? extends Number> method() { /* ... */ }
// Compiler error.
List<Number> list1 = method();
// OK, but yuk!
List<? extends Number> list2 = method();
// OK, but the list gets copied.
List<Number> list3 = new ArrayList<Number>(method());
局部变量中的通配符不是必需的(除了接受通配符返回方法的结果)。
引用Effective Java 2nd Ed:
如果一个类的用户必须考虑通配符类型,那么该类的API可能有问题。
答案 1 :(得分:2)
这确实没有多大意义。除非你想要的只是阅读它:
Set<?> unknownSet = new HashSet<String>();
System.out.println(unknownSet); // prints empty set: []
unknownSet.add("salala"); // compile error
另一方面,Diamond Operator更清洁,更有用:
Set<String> unknownSet = new HashSet<>();
unknownSet.add("salala"); // okay now
System.out.println(unknownSet); // prints single set: [salala]
最后,使用通用对象类型,您可以创建混合集。但那些很难调试:
Set<Object> unknownSet = new HashSet<>();
unknownSet.add("salala"); // adding a String
unknownSet.add(42); // adding an Integer
System.out.println(unknownSet); // prints set, sg like this: [42, salala]
答案 2 :(得分:1)
这是unbounded wildcard的情况。
有人提到它的优点:
如果您正在编写可以使用的方法 Object类中提供的功能。当代码使用时 泛型类中不依赖于类型参数的方法。 例如,List.size或List.clear。事实上,Class经常出现 因为Class中的大多数方法都不依赖于T。
但是在声明变量时,我同意它不是更有用,因为你的变量已经受到RHS方面的限制,事实上你无法向它添加String
元素,如另一个答案。无界通配符在上述链接中提到的示例中使用的printList
等方法中更有用。