[英]Java generic method wildcard mismatch issue
我想要实现的基本功能是将List<Ref<Thing>>
的引用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
之前? extends T
通配符结构,但是我做错了什么?
如果您将“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;
}
将您的方法声明为List<? extends Ref<? extends T>>
List<? extends Ref<? extends T>>
List<? extends Ref<? extends T>>
代替:
public <T> List<T> refsToObjects(List<? extends Ref<? extends T>> list) { ... }
没有什么东西可以在体内改变。
编辑:使用此解决方案,类型推断在呼叫站点似乎仍然失败。 它只适用于这样的调用this.<SuperThing>refsToObjects(refs)
。 因此,如果你可以期待这种用法,那么使用附加类型参数的wrm解决方案是更可取的。
我可以看到,代码中有两个错误。 首先是要相信List<Ref<? extends Thing>>
List<Ref<? extends Thing>>
是List<Ref<Thing>>
的超类型。 如果我们像DerivedThing
一样创建Thing
的子类,我们就不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>>
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>>
List<Ref<? extends Thing>>
为函数List<Ref<? extends Thing>>
List<Ref<? extends Thing>>
作为参数List<Ref<? extends Thing>>
,这将允许函数将一些无效项添加到列表中。
第二个错误是认为<? extends Thing>
的基类型(或擦除类型) <? extends Thing>
<? extends Thing>
是SuperThing
而不是剩下的Thing
。 在这里, <? extends 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>.
的超类List<Thing>.
请注意,我们可以将一个Ref<Thing>
添加到List<Ref<? extends SuperThing>>
List<Ref<? extends SuperThing>>
。 因此,如果您想将List<Ref<Thing>>
作为起点,请使用上述解决方案之一或使用wrm的解决方案 。
就个人而言,我更愿意在最大程度上使用多态,并始终引用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
类型的对象传递给Ref()
的构造函数中的SuperThing
类型的引用。 通过使用层次结构的超类作为所有派生对象的引用,所有编码变得更加简单。 当您选择主要仅通过其超类来查看所有对象时,OOP非常好并且非常容易,并且这扩展到泛型的使用。
类型必须完全匹配,因此:
List<Ref<? extends Thing>> refs = new ArrayList<Ref<? extends Thing>>();
List<SuperThing> objectList = refsToObjects(refs);
应该管用。
如果您执行以下操作,它也会起作用:
List<Ref<Thing>> refs;
List<SuperThing> objectList = this.<Thing>refsToObjects(refs);
发生的事情是该方法需要扩展T
东西。 但你永远不会定义T
@wrm解释的那个在方法中定义它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.