我想要实现的基本功能是将引用列表List<Ref<Thing>>
映射到实际对象的列表,但由超类List<SuperThing>
给出。在此示例中,Thing extends SuperThing
和Ref<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
通配符结构,但我做错了什么?
答案 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
的所有内容;甚至在创建Thing
或Ref<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解释的那个在方法中定义它。