Java通用方法通配符不匹配问题

时间:2013-01-03 09:28:49

标签: java generics

我想要实现的基本功能是将引用列表List<Ref<Thing>>映射到实际对象的列表,但由超类List<SuperThing>给出。在此示例中,Thing extends SuperThingRef<Thing>有一个方法public Thing get()来获取引用的对象。

我认为有效的方法:

public <T> List<T> refsToObjects(List<Ref<? extends T>> list) {
    List<T> result = new ArrayList<T>();
    for(Ref<? extends T> ref : list) {
        result.add(ref.get());
    }
    return result;
}

但是当我尝试使用它时

List<Ref<Thing>> refs;
List<SuperThing> objectList = refsToObjects(refs);

我收到以下错误消息:The method refsToObjects(List<Ref<? extends T>>) is not applicable for the arguments (List<Ref<Thing>>)

之前我没有主动使用? extends T通配符结构,但我做错了什么?

5 个答案:

答案 0 :(得分:5)

如果您将“extended”参数指定为通用参数,则它可以工作:

    public <T, S extends T> List<T> refsToObjects(List<Ref<S>> list) {
    List<T> result = new ArrayList<T>();
    for(Ref<S> ref : list) {
        result.add(ref.get());
    }
    return result;
}

答案 1 :(得分:3)

将您的方法声明为List<? extends Ref<? extends T>>代替:

public <T> List<T> refsToObjects(List<? extends Ref<? extends T>> list) { ... }

在体内不应该有任何改变。

使用此解决方案,

EDIT:类型推断在呼叫站点似乎仍然失败。它仅适用于this.<SuperThing>refsToObjects(refs)之类的调用。因此,wrm's solution使用其他类型参数是可取的,如果您可以期待这种用法。

答案 2 :(得分:2)

正如我所看到的,代码中有两个错误。第一个是相信类型List<Ref<? extends Thing>>List<Ref<Thing>>的超类型。如果我们创建Thing的子类DerivedThing,我们就无法将Ref<DerivedThing>的实例添加到List<Ref<Thing>>列表中:

List<Ref<Thing>> refs = new ArrayList<Ref<Thing>>();
refs.add(new Ref<Thing>()); // OK.
refs.add(new Ref<DerivedThing>()); // Error!

但是,如果我们将其替换为List<Ref<? extends Thing>>,那么课程DerivedThing就不再有问题了:

List<Ref<? extends Thing>> refs = new ArrayList<Ref<? extends Thing>>();
refs.add(new Ref<Thing>()); // Still OK.
refs.add(new Ref<DerivedThing>()); // Now OK!

因此,如果编译器允许将值List<Ref<? extends Thing>>传递给以List<Ref<? extends Thing>>为参数的函数,则允许该函数将一些无效项添加到列表中。 / p>

第二个错误是认为<? extends Thing>的基本类型(或删除类型)是SuperThing而不是Thing。这里,<? extends Thing>指定由Thing及其派生类组成的类型集合,而不是SuperThing及其派生类的集合。因此,我们可以写:

List<Ref<? extends Thing>> refs = new ArrayList<Ref<? extends Thing>>();
refs.add(new Ref<Thing>());
List<Thing> objectList = refsToObjects(refs);

或者,使用SuperThing作为删除类型:

List<Ref<? extends SuperThing>> refs = new ArrayList<Ref<? extends SuperThing>>();
refs.add(new Ref<Thing>());
List<SuperThing> objectList = refsToObjects(refs);

但不是两者的组合,因为List<SuperThing>不是List<Thing>.的超类请注意,我们可以向Ref<Thing>添加List<Ref<? extends SuperThing>>。因此,如果您希望将List<Ref<Thing>>作为起点,请使用上述解决方案之一或使用wrm's solution

就个人而言,我更喜欢在最大程度上使用多态,并始终引用SuperThing的所有内容;甚至在创建ThingRef<Thing>个对象时也是如此。例如,如果我们将T类型的参数添加到Ref()的构造函数中:

List<Ref<SuperThing>> refs = new ArrayList<Ref<SuperThing>>();
refs.add(new Ref<SuperThing>(new Thing()));
List<SuperThing> objectList = refsToObjects(refs);

请注意,我们现在将Thing类型的对象传递给SuperThing构造函数中Ref()类型的引用。通过使用层次结构的超类作为所有派生对象的引用,所有编码变得更加简单。当你选择通过他们的超类只看到所有对象时,OOP非常有效且非常容易,这延伸到泛型的使用。

答案 3 :(得分:0)

类型必须完全匹配,所以:

List<Ref<? extends Thing>> refs = new ArrayList<Ref<? extends Thing>>();
List<SuperThing> objectList = refsToObjects(refs);

应该有用。

答案 4 :(得分:0)

如果您执行以下操作,它也会起作用:

List<Ref<Thing>> refs;
List<SuperThing> objectList = this.<Thing>refsToObjects(refs);

正在发生的事情是该方法需要扩展T的内容。但你永远不会定义T。 @wrm解释的那个在方法中定义它。