我是Java新手。在this文档中,他们将此作为使用通配符的用例:
static void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
这是他们的解决方案:
static void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
但如果没有外卡,我也可以这样做:
static <T> void printCollection(Collection<T> c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
有人可以向我展示一个简单的用例,其中常规泛型不起作用,但外卡会吗?
更新:此处When to use wildcards in Java Generics?的答案并未告诉我们需要使用通配符。实际上是相反的。
答案 0 :(得分:4)
通配符允许我们做的一件事是声明对特定类型参数不可知的类型,例如&#34;任何类型列表的列表&#34; :
List<List<?>> listOfAnyList = ...;
listOfAnyList.add( new ArrayList<String>() );
listOfAnyList.add( new ArrayList<Double>() );
如果没有通配符,这是不可能的:*因为元素列表可能彼此具有不同的类型。
如果我们尝试capture,我们会发现我们无法:
static <E> void m(List<List<E>> listOfParticularList) {}
m( listOfAnyList ); // <- this won't compile
另一件事通配符允许我们做那个类型参数不能设置下限。 (类型参数可以使用extends
绑定声明,但不能绑定super
。**)
class Protector {
private String secretMessage = "abc";
void pass(Consumer<? super String> consumer) {
consumer.accept( secretMessage );
}
}
假设pass
被声明为Consumer<String>
。现在假设我们有一个Consumer<Object>
:
class CollectorOfAnything implements Consumer<Object> {
private List<Object> myCollection = new ArrayList<>();
@Override
public void accept(Object anything) {
myCollection.add( anything );
}
}
问题是:我们无法将其传递给接受Consumer<String>
的方法。声明Consumer<? super String>
表示我们可以通过任何接受String
的消费者。 (另见Java Generics: What is PECS?。)
大多数时候,通配符只是让我们做出整洁的声明。
如果我们不需要使用某种类型,我们就不必为其声明类型参数。
*技术上也可以使用raw type,但不鼓励使用原始类型。
**我不知道为什么Java不允许super
类型参数。 4.5.1. Type Arguments of Parameterized Types可能暗示它与类型推断的限制有关:
与方法签名中声明的普通类型变量不同,使用通配符时不需要类型推断。因此,允许在通配符[...]上声明下限。
答案 1 :(得分:0)
T代表该数据结构的泛型类型。在你的上一个例子中,你不使用它,它不是一个实际的类型(例如String),并且因为你不使用它,在这种情况下它并不重要。
例如,如果你有一个Collection并试图将它传递给一个接受Collection的方法,那就行了,因为类路径上没有类型T所以它被认为是一个变量。如果您尝试将相同的Collection传递给接受Collection的方法,那么这将无效,因为您的类路径上有String,因此它不是变量。
答案 2 :(得分:0)
以列表为例。
List<?>
可以是List<A>
的父类。例如,
List<B> bList = new ArrayList<>(); // B is a class defined in advance
List<?> list = bList;
在这种情况下,你永远不能使用<T>
。
<?>
有通配符捕获。这里,
void foo(List<?> i) {
i.set(0, i.get(0));
}
上面的代码无法编译。你可以解决它:
void foo(List<?> i) {
fooHelper(i);
}
// wildcard can be captured through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
了解详情,http://docs.oracle.com/javase/tutorial/java/generics/capture.html
我现在只能想到这两个,后来可能会更新。