List<Something>和List<? extends Something>有什么区别?

5
这两个List有什么区别吗?
List<Something> a;
List<? extends Something> b;

我知道,例如
List<Object> a;
List<?> b;
List c;

并且

List<Something> a;
List<? extends SomeInterfaceSomethingImplements> b;

这些都是不同的,但如果将Something替换为Object,它们是否不同呢?

List<Object> a;
List<? extends Object> b;

请参见:https://dev59.com/FnNA5IYBdhLWcg3wmfIa - Diego Basch
将字段的类型设置为List<Object>只允许您将List<Object>分配给它。由于类型擦除,List<String>不被视为List<Object>。声明为List<? extends Object>的引用可以分配属于Object的任意子类的元素列表(对于Java中的所有类都成立)。这可能意味着List<Object>List<String>List<Whatever> - toniedzwiedz
@Tom 对,除了我非常确定这不是由于类型擦除。StringObject已经存在,只是被检查了,与擦除类型无关。 - Bernhard Barker
我不认为这是一个重复的问题。在另一个问题中,提问者正在比较<Date><? extends Serializable>。而在我的情况下,例如比较的是<Date><? extends Date> - PurkkaKoodari
4个回答

11
List<Something>是一个类型的对象列表。 List<? extends Something>是一种特定类型的对象列表,该类型扩展了Something
所以,List<Object>可以拥有任何扩展了Object的类的对象。 但是,List<? extends Object>只能被初始化为或赋值为该特定类的对象列表,该类扩展了Object
这在什么情况下有用呢?比如说你想编写一个方法,该方法接受一个ObjectsList并打印每个项:
void print(List<Object> list) {
    for (Object obj: list) {
        System.out.println(obj)
    }
}

现在假设您有一个List<Integer>。您无法将其传递给上述方法,因为print需要的是List<Object>,而List<Integer>无法分配给List<Object>。为了解决这个问题,我们重新定义print如下:

void print2(List<? extends Object> list) {
    for (Object obj: list) {
        System.out.println(obj)
    }
}

现在,我们可以将任何Object的子类的列表传递给print2函数。 print2会接受List<Integer>List<String>等。

然而,在print2内部你不能向列表中添加任何内容,因为print2不知道使用的具体的Object子类型。因此,只能在不需要向List添加任何内容的方法中使用? extends ...


List<? extends Object> 也可以初始化为 List<Object> - Ryan
@Ryan,是的,它可以。? 的有用之处在于它也可以被分配给 List<Integer>,而你不能将 List<Integer> 分配给 List<Object> - Hari Menon
第二段第二句话的措辞听起来好像不可能。 - Ryan

2
正如其他人所提到的,List<Something> 是一个类型为 Something 的对象列表,而 List<? extends Something> 则会被初始化为不同类型的对象列表,只要该对象类型扩展了 SomethingList<Object> 是一个类型为 Object 的对象列表。 List<? extends Object> 可以被初始化为任何扩展了 Object 的数据类型的对象列表。
下面是一些代码,展示了使用 Object? extends Object 时会发生什么:
import java.util.ArrayList;

public class test {
    class A {}

    public static void main(String[] args) {
        // No error
        ArrayList<Object> arrObj = new ArrayList<Object>();

        // Error
        ArrayList<Object> arrObj2 = new ArrayList<A>();

        // No Error
        ArrayList<? extends Object> arrExt = new ArrayList<Object>();

        // No Error
        ArrayList<? extends Object> arrExt2 = new ArrayList<A>();
    }

}

1

List<Something> 表示实现应该类似于 ArrayList<Something>。但是另一种情况下,List<? extends Something> 表示实现可以是任何东西,例如 ArrayList<Something> 或者 ArrayList<SomethingChild>,其中 SomethingChild 继承自 Something

您可以进行赋值操作

List<? extends Something> list = new ArrayList<SomethingChild>();

虽然您无法分配

List<Something> list = new ArrayList<SomethingChild>();

这将导致编译错误。
您可以了解有关泛型和通配符在泛型中使用的更多信息,以获得一些洞察力。

0

第一个是某种对象类型的集合,第二个是某种子类型的对象集合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接