我主要是一名C#开发人员,我正在向我的朋友教授数据结构,他们在大学里使用Java,我在Java中看到了这样的表达式:
void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
我没有在C#中看到这样的东西,所以我想知道Java中Collection<T>
和Collection<?>
之间的区别是什么?
void printCollection(Collection<T> c) {
for (Object e : c) {
System.out.println(e);
}
}
我认为它也可以用上面的方式编写。文档中的人正在比较Collection<Object>
和Collection<T>
。
示例来自http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
答案 0 :(得分:26)
Collection<?>
是未知类型参数的集合。
就调用者而言,
之间没有区别void printCollection(Collection<?> c) { ... }
和
<T> void printCollection(Collection<T> c) { ... }
但是,后者允许实现引用集合的类型参数,因此通常是首选。
之前的语法存在是因为并不总是可以在适当的范围内引入类型参数。例如,考虑:
List<Set<?>> sets = new ArrayList<>();
sets.add(new HashSet<String>());
sets.add(new HashSet<Integer>());
如果我要用某个类型参数?
替换T
,sets
中的所有集都将被限制为相同的组件类型,即我不能再将具有不同元素的集合置于类型列在同一个列表中,如以下尝试所示:
class C<T extends String> {
List<Set<T>> sets = new ArrayList<>();
public C() {
sets.add(new HashSet<String>()); // does not compile
sets.add(new HashSet<Integer>()); // does not compile
}
}
答案 1 :(得分:19)
声明Collection<?>
(发音为“unknown of unknown”)是一个元素类型与任何东西匹配的集合,而Collection<T>
代表T
类型的集合。
像往常一样,Angelika Langer的FAQ on generics对该主题进行了广泛的讨论,这是必须阅读的,以便充分了解Java中的泛型,以及特别是无界的通配符(这个问题的主题)。引用常见问题解答:
无界的通配符看起来像“?”,代表所有类型的家庭。无界通配符用作泛型类型实例化的参数。在不需要了解参数化类型的类型参数的情况下,无界通配符非常有用
有关详细技术详情,请查看§4.5.1 Type Arguments and Wildcards的Java Language Specification部分,其中说明:
类型参数可以是引用类型或通配符。在只需要部分了解类型参数的情况下,通配符非常有用。
答案 2 :(得分:5)
使用Collection<T>
即可
void printCollection(Collection<T> c) {
for (T e : c) {
System.out.println(e);
}
}
使用Collection<?>
,您只知道该集合包含对象。
答案 3 :(得分:5)
使用无界通配符(?)的那个实际上意味着? extends Object
(任何扩展Object的东西)。
这在Java中意味着只读属性,即允许我们从通用结构中读取项目,但我们不允许在其中放回任何内容,因为我们无法确定实际的类型其中的元素。
因此,我敢说,至少在我们遇到需要假设某种类型的情况之前,它在所讨论的printCollection
方法中是一种非常有效的方法。
如果必须在它们之间进行选择,我会说第二个(带有类型参数T)是一种更干净的方法,因为你至少可以假设该集合具有某种类型T
,并且可以证明在某些情况下有用。
例如,如果需要同步Collection,我可以简单地执行:
<T> void printCollection(Collection<T> c) {
List<T> copy = new ArrayList<T>();
synchronized(c) {
copy.addAll(c);
}
for (T e : copy) {
System.out.println(e);
}
}
我很容易创建一个类型为T
的新集合,并将原始集合的所有元素复制到第二个集合中。我可以这样做,因为我可以假设集合的类型是T
,而不是?
。
当然,我可以用第一种方法(使用ubounded通配符)做同样的事情,但它不是那么干净,我不得不假设Collection的类型是Object,而不是{{1} }(不能有效地用作类型参数: )。?
new ArrayList<?>()