Java泛型上限通配符解惑:为何add方法编译失败?
List<? extends Object>不能添加字符串? 这是Java泛型里一个特别容易踩的坑,我来给你掰扯清楚为啥这段代码编译不过~
先看你的代码:
public static void addSound(List<? extends Object> list) { list.add("quack"); // 编译失败 }
你可能觉得“? extends Object应该能接受所有Object子类的List,String是Object子类,那往里面加字符串没问题啊?” 其实这里的关键是:List<? extends Object>表示的是「某一种特定的、元素类型是Object子类的List」,而不是「所有能装Object子类的List」。
举个具体的例子:假设我这样调用你的方法:
List<Integer> intList = new ArrayList<>(); addSound(intList);
这完全符合方法的参数要求,因为Integer是Object的子类,List<Integer>是List<? extends Object>的子类。但这时候如果addSound里的list.add("quack")能执行,就等于把一个String塞进了List<Integer>里,这明显违反了泛型的类型安全,编译器绝对不会允许这种情况发生。
本质上,上限通配符? extends T是为「只读场景」设计的:你可以从这个列表里读取元素,并且安全地把它们当成T类型(比如这里的Object)来使用,但你不能往里面添加任何具体类型的元素(除了null,因为null是所有类型的默认值)——因为编译器根本不知道这个列表实际是存哪种类型的。
如果你的需求就是要往列表里添加任意Object子类的元素,那应该直接用List<Object>作为参数类型,而不是带上限通配符的版本:
public static void addSound(List<Object> list) { list.add("quack"); // 编译通过 }
这时候编译器明确知道列表的元素类型是Object,String是Object的子类,所以添加操作完全合法。
内容的提问来源于stack exchange,提问作者san

