Java:再次通配符

2022-09-03 04:45:36

在我的最后一个问题中(谢谢所有回答我的人),我已经了解了 和 之间的区别。List<Object>List<?>

但是,我仍然看不到通配符的有用性。

我有两个:ArrayList

ArrayList<Integer> li = new ArrayList<Integer>(Arrays.asList(1,2,3));
ArrayList<String> ls = new ArrayList<String>(Arrays.asList("one","two","three"));

现在,看看下面的两个代码块:

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

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

当我打电话时:

printList(li);
printList(ls);

这两种方法都返回输出:

1 2 3
one two three

然而,第二种解决方案,在for循环中,我使用参数化类型而不是s(我认为更优雅)。Object

因此,主要问题仍然存在:为什么我们需要通配符


答案 1

如果问题是“通配符的有用性”:

当只需要有关类型参数的部分知识时,通配符很有用。“部分知识”由上限和下限实现(?超级T或?扩展T);如果你只使用未绑定的通配符( ? ),你的意思是根本没有知识,你看不到通配符真正有用的地方。

通配符可以与类型参数一起组合使用,以在方法参数类型,返回类型和异常类型之间创建关系。因此,有用通配符的一个示例是

 class ListManager<T> {
    public void add(T item, List<? super T> list) {
        [... some useful operation ...]
         list.add(item);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<Object>();
        Integer item = 10;
        ListManager<Integer> manager = new ListManager<Integer>();
        manager.add(item, list);
    }
}

方法“ListManager.add()”在“list”类型和“item”类型之间创建关系。对“list”的操作始终是类型安全的,但是您可以将方法“add”与不同参数类型的列表一起使用:我们对参数“list”使用了最小约束。

(另请参阅 jls7 文档)


答案 2

Java泛型教程中,它说:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

由于我们不知道c的元素类型代表什么,因此我们无法向其添加对象。add() 方法采用类型 E(集合的元素类型)的参数。当实际类型参数为 ?时,它代表某个未知类型。我们传递到添加的任何参数都必须是此未知类型的子类型。由于我们不知道那是什么类型,因此我们无法传入任何内容。唯一的例外是 null,它是每种类型的成员。

另一方面,给定一个 List,我们可以调用 get() 并利用结果。结果类型是未知类型,但我们始终知道它是一个对象。因此,将 get() 的结果分配给 Object 类型的变量或将其作为参数传递到对象类型是安全的。

因此,使用通配符类型可确保我们无法进入列表,但 .如果你只是在列表上调用,你知道它至少是一个对象。add()nullget()


推荐