为什么要将变量声明为通配符类型

Why declare a variable as a wildcard type

Java tutorials中,有时会这样写:

Set<?> unknownSet = new HashSet<String>();

虽然我了解在 class 定义和方法中使用类型参数和通配符的好处,但我想知道以下问题:

这确实没有多大意义。除非你唯一想要的就是阅读它:

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]

unbounded wildcard就是这种情况。

那里提到了它的优点:

If you are writing a method that can be implemented using functionality provided in the Object class. When the code is using methods in the generic class that don't depend on the type parameter. For example, List.size or List.clear. In fact, Class is so often used because most of the methods in Class do not depend on T.

但是 当声明一个变量时,我同意它没有任何用处,因为你的变量已经被 RHS 端限制并且事实上你不能添加 String 元素到正如另一个答案中指出的那样。 unbounded wildcard在像上面link.

例子中使用的printList这样的方法中比较有用

通配符仅在方法参数声明中真正有用,因为它们增加了可接受的参数类型的范围,例如:

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.

return 类型中的通配符让您的 class 用户的生活变得困难(或者更确切地说,变得混乱),因为您必须传播它们,或者做明确的工作来让它们消失,例如:

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());  

局部变量中的通配符不是必需的(除了接受通配符-returning 方法的结果)。

引用 Effective Java 第二版:

If the user of a class has to think about wildcard types, there is probably something wrong with the class’s API.