Collection <?>和Collection <T>之间有什么区别?

Tar*_*rik 23 java generics collections types unbounded-wildcard

我主要是一名C#开发人员,我正在向我的朋友教授Data Structures,他们在他们的大学里使用Java,我在Java中看到了这样一个表达式:

void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

我还没有在C#中看到这样的东西,所以我想知道Java Collection<T>Collection<?>Java 之间的区别是什么?

void printCollection(Collection<T> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为它也可以用上面的方式编写.在文档中的家伙比较Collection<Object>Collection<T>虽然.

示例来自http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

mer*_*ike 26

Collection<?> 是未知类型参数的集合.

就调用者而言,两者之间没有区别

void printCollection(Collection<?> c) { ... }
Run Code Online (Sandbox Code Playgroud)

<T> void printCollection(Collection<T> c) { ... }
Run Code Online (Sandbox Code Playgroud)

但是,后者允许实现引用集合的类型参数,因此通常是首选.

之前的语法存在是因为并不总是可以在适当的范围内引入类型参数.例如,考虑:

List<Set<?>> sets = new ArrayList<>();
sets.add(new HashSet<String>());
sets.add(new HashSet<Integer>());
Run Code Online (Sandbox Code Playgroud)

如果我要用?某个类型参数替换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
    }
}
Run Code Online (Sandbox Code Playgroud)


Ósc*_*pez 19

声明Collection<?>(发音为"未知集合")是一个集合,其元素类型与任何东西匹配,而Collection<T>代表类型集合T.

像往常一样,Angelika Langer 关于泛型常见问题解答对该主题进行了广泛的讨论,这是必须阅读的,以便充分理解Java中的泛型,特别是无界的通配符(这个问题的主题).引用常见问题解答:

无界的通配符看起来像"?",代表所有类型的家庭.无界通配符用作泛型类型实例化的参数.在不需要有关参数化类型的类型参数的知识的情况下,无界通配符非常有用

有关更多技术细节,请查看Java语言规范的§4.5.1类型参数和通配符部分,其中指出:

类型参数可以是引用类型或通配符.通配符在需要仅部分了解类型参数的情况下非常有用.

  • 无论如何,由于[类型擦除](http://docs.oracle.com/javase/tutorial/java/generics/erasure.html),这两种形式在运行时完全等效. (9认同)

Don*_*oby 5

Collection<T>你可以做

void printCollection(Collection<T> c) {
    for (T e : c) {
        System.out.println(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

有了Collection<?>你只知道集合包含的对象.


Edw*_*rzo 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我很容易创建一个新的类型集合T,并将原始集合的所有元素复制到第二个集合中.我可以这样做,因为我可以假设集合的类型是T,而不是?.

当然,我可以用第一种方法(使用ubounded通配符)做同样的事情,但它不是那么干净,我不得不假设Collection的类型是Object,而不是?(它不能被有效地用作类型参数:) new ArrayList<?>().

void printCollection(Collection<?> c) {
    List<Object> copy = new ArrayList<Object>();
    synchronized(c) {
        copy.addAll(c);
    }
    for (Object e : copy) {
        System.out.println(e);
    }
}
Run Code Online (Sandbox Code Playgroud)