繁体   English   中英

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

[英]Java generic method wildcard mismatch issue

我想要实现的基本功能是将List<Ref<Thing>>的引用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 之前? 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所有内容; 甚至在创建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类型的对象传递给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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM