List<? extends MyType>

42

我有一个关于Java泛型的问题。我声明了一个泛型列表:

List<? extends MyType> listOfMyType;

然后在某个方法中,我尝试实例化并向该列表添加项目:

listOfMyType = new ArrayList<MyType>();
listOfMyType.add(myTypeInstance); 

myTypeInstanceMyType类型的一个对象时,它不能编译。 它显示以下错误:

在类型为List<capture#3-of? extends MyType>的add(capture#3-of? extends MyType)方法不适用于(MyType)参数

有任何想法吗?


1
如果类Bar扩展类Foo,而类Baz具有generic<T>,那么Baz<Foo> waldo = new Baz<Bar>()将不起作用,因为Baz<Bar>不是Baz<Foo>的子类,Bar是Foo的子类。子类化不适用于泛型。ArrayList<Object> flob = new ArrayList<Double>()也不起作用。简而言之,继承不适用于泛型。由于您使用List<? extends MyType>,因此可以有List<Some sub type> = new ArrayList<MyType>。由于它们不相同,所以不好。 - JustAFellowCoder
5个回答

34

11
正如Josh Bloch所说:“记住PECS:Producer Extends,Consumer Super。”由于listOfMyType是一个消费者(你正在添加到它),super是有效的,而extends则无效。 - Michael Myers
1
小题大做:你可以使用“extends”来“put”“null”。此外,你还可以捕获类型并“put”之前获取的引用。 - Tom Hawtin - tackline

28

考虑以下代码:

class MySubType extends MyType {
}

List<MySubType> subtypeList = new ArrayList<MySubType>();
List<? extends MyType> list = subtypeList;
list.add(new MyType());
MySubType sub = subtypeList.get(0);

sub现在包含一个MyType,这是非常错误的。


6
您的情况下不需要使用通配符捕获语法,只需声明即可。
List<MyType> listOfMytype;

应该足够了。如果您想知道确切的原因,Java泛型教程提供了比您想要了解的更多关于Java泛型的奥秘疯狂的内容。第20页涉及了您特定的情况。
至于为什么使用通配符捕获进行添加操作不起作用,这是因为编译器无法确定列表中的MyType子类在每种情况下都是什么,因此编译器会发出错误提示。

3

我不知道这是否真的对你有所帮助,但这是我在调用Spring Framework的通用方法并想要返回通用列表时必须使用的内容:

public <T> List<T> findAll(String tableName,Class<?> table) {
    String sql = "SELECT * FROM "+ tableName ;
    List<?> entities = getSimpleJdbcTemplate().query(sql,
            ParameterizedBeanPropertyRowMapper.newInstance(table));
            return (List<T>) entities;
}

看起来参数化需要你在列表中使用?符号来接收结果,然后将列表转换为预期的返回类型。

我仍然被泛型搞得晕头转向...


1
不要通过字符串拼接创建SQL查询 - 请注意SQL注入。 - TajnosAgentos

3
这里有一个类似的讨论帖子:如何向通配符泛型集合添加元素? 了解泛型的工作原理可以参考以下示例:
    List<SubFoo> sfoo = new ArrayList<SubFoo>();
    List<Foo> foo;
    List<? extends Foo> tmp;

    tmp = sfoo;
    foo = (List<Foo>) tmp;

问题在于,它并不是为本地/成员变量设计的,而是为函数签名设计的,这就是为什么它如此反常。


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