29

有人可以解释一下这两种方法有什么区别吗?他们是一样的吗?就解决的问题而言,它们在我看来确实相同。如果它们相同,为什么需要?

方法#1,无界

public static void printList(List<?> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

方法#2,无界:

public static <T> void printList(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

方法#1,有界

public static void printList(List<? extends Number> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

方法#2,有界:

public static <T extends Number> void printList(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}
4

3 回答 3

34

它们的相同之处在于它们接受相同的参数类型。

但是,使用(或其他)标识类型T可以让您在其他地方引用该类型。

编辑:示例:

您的无限示例没有充分利用参数化类型的功能。你有:

public static <T> void printList(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

这对于打印字符串表示的示例来说已经足够了,但是考虑一下(非常做作,并且没有错误处理):

public static <T> T getSecondItem (List<T> list) {
    T item = list.get(1);
    return item;
}

返回类型是 T,它允许您通过编译时类型检查安全地执行以下操作:

class MyClass {
    public void myMethod () { }
}

void somewhere () {
    List<MyClass> items = ...;
    getSecondItem(items).myMethod();
}

命名类型还允许您在多个位置共享相同的类型约束,例如:

public <T> int compareLists (List<T> a, List<T> b) {
    ...
}

如果您没有命名类型,则无法指定与ab是相同列表类型的约束(您可以使用List<? extends T>以获得更大的灵活性)。

您还问“我为什么需要??”。真正的答案是:你没有。我想这主要是为了美观。Java 力求成为一种精确且整洁的语言。在很多情况下,您根本不关心您指的是什么类型。在这些情况下,您可以使用?带有未使用的类型参数声明的代码而不会造成混乱。

于 2013-08-14T20:53:16.533 回答
6

在您的示例中,完全没有区别。每个都将产生相同的输出。

泛型的最佳使用和解释要求您了解类型参数的语义以及有关参数角色的一些信息。这样做的原因是,在上面的第一个示例(无界通配符)这样的情况下,语义是“未知类型的对象列表”和“将产生(不消耗)List<?>实例的参数。

上面的方法只是生成每个List<?>对象并调用每个对象toString()。所有对象都保证有一个toString()方法,因此根本不需要为此目的了解对象的类型。这正是为什么无界通配符是此方法参数的最佳选择的原因:要生成List<?>实例并调用toString()它们,无需了解对象类型的任何信息。

请注意,如果?具有相同的语义(“未知类型的对象列表”)但目的不同(“列表将消耗未知类型的对象”),事情将会非常非常彻底地改变,以至于它可能是不可取的(或非常困难)使用通配符参数(至少没有辅助方法来捕获对象的类型)。

通常不可能为所有情况断言一种通用参数形式。使用的最佳形式(通配符与具体;有界与无界;扩展与超级)取决于类型参数的语义以及参数在方法中的作用。

于 2013-08-14T21:14:38.453 回答
5

如上所述,?通用代码不需要对类型的任何引用。正如 Java Trails 中所指出的,这可能是一个属性独立于任何类型的类,例如集合的长度。

另一个原因?是它为通用代码只需要类 Object 提供的行为的情况提供了更少歧义的语法。正如上面所指出的,参数类型<T>可以工作,但是 T 被定义然后从未使用过的事实可能表明开发人员遗漏了一些东西。

因此,如果<T>由于语义模糊而被丢弃并且<?>不可用,那么开发人员会很想编写<Object>. 但是,这不允许将泛型代码用于任何类型,只能用于 Object 类型。

所以,<?>更准确。

于 2013-12-20T06:38:35.907 回答