简体   繁体   English

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

[英]Java generic method wildcard mismatch issue

The basic thing I want to achieve is to map a list of references List<Ref<Thing>> to a list of the actual objects, but given by the superclass List<SuperThing> . 我想要实现的基本功能是将List<Ref<Thing>>的引用List<Ref<Thing>>映射到实际对象的列表,但是由超类List<SuperThing> In this example, Thing extends SuperThing and Ref<Thing> has a method public Thing get() to get the referenced object. 在这个例子中, Thing extends SuperThingRef<Thing>有一个方法public Thing get()来获取引用的对象。

The method that I assumed valid: 我认为有效的方法:

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;
}

But when I try to use it 但是当我尝试使用它时

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

I get this error message: The method refsToObjects(List<Ref<? extends T>>) is not applicable for the arguments (List<Ref<Thing>>) 我收到此错误消息: The method refsToObjects(List<Ref<? extends T>>) is not applicable for the arguments (List<Ref<Thing>>)

I did not actively use the ? extends T 我没有积极使用? extends T ? extends T wildcard structure before, but what am I doing wrong? 之前? extends T通配符结构,但是我做错了什么?

It works, if you specify the "extended" parameter also as generic parameter: 如果您将“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;
}

Declare your method as taking List<? extends Ref<? extends T>> 将您的方法声明为List<? extends Ref<? extends T>> List<? extends Ref<? extends T>> List<? extends Ref<? extends T>> instead: List<? extends Ref<? extends T>>代替:

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

Nothing should have to change within the body. 没有什么东西可以在体内改变。

EDIT: type inference still seems to fail at the call site using this solution. 编辑:使用此解决方案,类型推断在呼叫站点似乎仍然失败。 It only works with a call like this.<SuperThing>refsToObjects(refs) . 它只适用于这样的调用this.<SuperThing>refsToObjects(refs) So wrm's solution using an additional type parameter is preferable if you can expect this kind of usage. 因此,如果你可以期待这种用法,那么使用附加类型参数的wrm解决方案是更可取的。

As I can see it, there are two errors in your code. 我可以看到,代码中有两个错误。 The first is to believe that the type List<Ref<? extends Thing>> 首先是要相信List<Ref<? extends Thing>> List<Ref<? extends Thing>> is a supertype for List<Ref<Thing>> . List<Ref<? extends Thing>>List<Ref<Thing>>的超类型。 If we create a subclass of Thing like DerivedThing , we cannot add an instance of Ref<DerivedThing> to a list of 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!

However, if we replace this with List<Ref<? extends Thing>> 但是,如果我们将其替换为List<Ref<? extends Thing>> List<Ref<? extends Thing>> then there is no more problem with the class DerivedThing : 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!

Therefore, if the compilator was to allow to pass of a value of List<Ref<? extends Thing>> 因此,如果编译器允许传递List<Ref<? extends Thing>>的值List<Ref<? extends Thing>> List<Ref<? extends Thing>> to a function taking List<Ref<? extends Thing>> List<Ref<? extends Thing>>为函数List<Ref<? extends Thing>> List<Ref<? extends Thing>> as its argument, this would allow the function to add some invalid item to the list. List<Ref<? extends Thing>>作为参数List<Ref<? extends Thing>> ,这将允许函数将一些无效项添加到列表中。

The second error is to think that the base type (or erasure type) of <? extends Thing> 第二个错误是认为<? extends Thing>的基类型(或擦除类型) <? extends Thing> <? extends Thing> is SuperThing instead of remaining Thing . <? extends Thing>SuperThing而不是剩下的Thing Here, <? extends Thing> 在这里, <? extends Thing> <? extends Thing> designates the collection of type composed of Thing and its derived classes and not the collection of SuperThing and its derived classes. <? extends Thing>指定由Thing及其派生类组成的类型集合,而不是SuperThing及其派生类的集合。 Therefore, we could write: 因此,我们可以写:

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

or, with SuperThing as the erasure type: 或者,使用SuperThing作为擦除类型:

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

but not a combination of both because List<SuperThing> is not a superclass for List<Thing>. 但不是两者的组合,因为List<SuperThing>不是List<Thing>.的超类List<Thing>. Notice that we can stil add a Ref<Thing> to a List<Ref<? extends SuperThing>> 请注意,我们可以将一个Ref<Thing>添加到List<Ref<? extends SuperThing>> List<Ref<? extends SuperThing>> . List<Ref<? extends SuperThing>> Therefore, use of one of the above solution or use the wrm's solution if you want to keep List<Ref<Thing>> as your starting point. 因此,如果您想将List<Ref<Thing>>作为起点,请使用上述解决方案之一或使用wrm的解决方案

Personally, I would prefer to use polymorphism at its fullest extend and always reference everything from a SuperThing ; 就个人而言,我更愿意在最大程度上使用多态,并始终引用SuperThing所有内容; even when creating Thing or Ref<Thing> objects. 甚至在创建ThingRef<Thing>对象时。 For example, if we add a parameter of type T to the constructor of Ref() : 例如,如果我们将类型T的参数添加到Ref()的构造函数中:

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

Note that we are now passing an object of type Thing to a reference of type SuperThing in the constructor of Ref() . 请注意,我们现在将一个Thing类型的对象传递给Ref()的构造函数中的SuperThing类型的引用。 By using the superclass of the hierarchy as the reference for all the derived objects, all the coding become much more simpler. 通过使用层次结构的超类作为所有派生对象的引用,所有编码变得更加简单。 OOP works very well and very easily when you choose to see all the objects mostly only through their superclass and this extends to the use of generic. 当您选择主要仅通过其超类来查看所有对象时,OOP非常好并且非常容易,并且这扩展到泛型的使用。

The types must match exactly, so: 类型必须完全匹配,因此:

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

should work. 应该管用。

It will also work if you do something like this: 如果您执行以下操作,它也会起作用:

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

What is happening is that the method expects something which extends T . 发生的事情是该方法需要扩展T东西。 but you never define T . 但你永远不会定义T The one explained by @wrm defines it in the method. @wrm解释的那个在方法中定义它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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