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

时间:2016-08-10 14:51:03

标签: java generics wildcard

Java tutorials中,有时会写出这样的内容:

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

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

  • 为变量提供包含通配符的类型有什么好处?
  • 在现实生活中,人们会这样做吗?何时?

3 个答案:

答案 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等方法中更有用。