3

我想要实现的基本目标是将引用列表映射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通配符结构,但我做错了什么?

4

5 回答 5

5

如果您将“扩展”参数也指定为通用参数,它会起作用:

    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;
}
于 2013-01-03T09:59:55.260 回答
3

将您的方法声明为采取List<? extends Ref<? extends T>>

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

体内不应该有任何改变。

编辑:类型推断似乎仍然在使用此解决方案的呼叫站点失败。它只适用于像this.<SuperThing>refsToObjects(refs). 因此,如果您可以预期这种用法,那么使用附加类型参数的wrm 解决方案是更可取的。

于 2013-01-03T14:05:37.390 回答
2

如我所见,您的代码中有两个错误。首先是相信该类型List<Ref<? extends Thing>>是 的超类型List<Ref<Thing>>。如果我们创建 like 的子ThingDerivedThing,我们不能将 的实例添加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>>then 类就没有问题了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>>,这将允许该函数将一些无效项添加到列表中。

第二个错误是认为的基本类型(或擦除类型)<? 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>. 请注意我们仍然可以将 a 添加Ref<Thing>到 a List<Ref<? extends SuperThing>>。因此,如果您想保留作为起点,请使用上述解决方案之一或使用wrm 的解决方案。List<Ref<Thing>>

就个人而言,我更愿意充分利用多态性,并始终引用 a 中的所有内容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的引用。通过使用层次结构的超类作为所有派生对象的引用,所有编码变得更加简单。当您选择主要只通过它们的超类查看所有对象时,OOP 工作得非常好并且非常容易,这扩展到了泛型的使用。SuperThingRef()

于 2013-01-03T18:01:56.097 回答
0

类型必须完全匹配,因此:

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

应该管用。

于 2013-01-03T09:44:14.440 回答
0

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

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

正在发生的事情是该方法需要扩展的东西T。但你从不定义T. @wrm 解释的那个在方法中定义了它。

于 2013-01-03T11:58:19.807 回答