类继承:泛型扩展泛型

时间:2017-04-25 13:36:13

标签: java generics inheritance

假设我有简单的课程

public class MyObject {
}

用于处理MyObject的子类的处理程序接口

public interface MyObjectHandler<V extends MyObject>{
     List<V> handle(List<V> objects);
}

假设我有BigObjects和SmallObjects(它们都扩展了MyObject),我想为它们分别设置处理程序。因此,我使用特定泛型创建MyObjectHandler的两个接口。

class BigObject extends MyObject {}
class SmallObject extends MyObject {}

// Handlers interfaces
interface BigObjectHandler extends MyObjectHandler<BigObject>{}
interface SmallObjectHandler extends MyObjectHandler<SmallObject>{}

// Concrete handlers
class BigHandler1 implements BigObjectHandler {....}
class BigHandler2 implements BigObjectHandler {....}

class SmallHandler1 implements SmallObjectHandler {....}
class SmallHandler2 implements SmallObjectHandler {....}

现在让我们假设我们创建了AbstractHandlerChain<...>抽象类。因此,我们可以创建BigHandlerChain类并注入我们的BigHandlers(和SmallHandlerChain一样)。

class BigHandlerChain extends AbstractHandlerChain<BigObjectHandler> {
    // Inject only childs of BigObjectHandler. E.g. via spring @Autowired
    public BigHandlerChain(List<BigObjectHandler> handlers) {
        this.handlers = handlers;
    }
} 

问题:是否可以为这种情况创建完美的AbstractHandlerChain?

可能的解决方案1 ​​

public abstract class HandlerChain<T extends MyObjectHandler> {

    private List<T> handlers;

    public HandlerChain(List<T> handlers) {
        this.handlers = handlers;
    }

    public <V extends MyObject> List<V> doChain(List<V> objects) {
        for (T handler : handlers) {
            objects = handler.handle(objects);
        }
        return objects;
    }
}

这个有效,但在handler.handle(objects)我得到Unchecked call to 'handle(List<V>)' as a member of raw type 'MyObjectHandler',所以我应该添加@SuppressWarnings("unchecked"),这不是很好。

可能的解决方案2

public abstract class HandlerChain<T extends MyObjectHandler<? extends MyObject>> {

    ...

    public <V extends MyObject> List<V> doChain(List<V> objects) {
        for (T handler : handlers) {
            objects = handler.handle(objects);
        }
        return objects;
    }
}

不起作用。在handler.handle(objects)我得handle (java.util.List<capture<? extends MyObject>>) cannot be applied to (java.util.List<V>)。为什么在这种情况下我无法将对象传递给处理程序?通配符扩展MyObject,V扩展MyObject。不是吗?

可能的解决方案3

public abstract class HandlerChain<T extends MyObjectHandler<V>, V extends MyObject> {

    ...

    public List<V> doChain(List<V> objects) {
        for (T handler : handlers) {
            objects = handler.handle(objects);
        }
        return objects;
    }
}

这个有效,但在这种情况下,我应该将BigHandlerChain定义为class BigHandlerChain extends AbstractHandlerChain<BigObjectHandler, BigObject>。但是BigObjectHandler已经包含了可以由它处理的类的信息,因此它是信息重复。

可能的解决方案4

public abstract class HandlerChain<T extends MyObjectHandler<V extends MyObject>> {

    ...

    public List<V> doChain(List<V> objects) {
        for (T handler : handlers) {
            objects = handler.handle(objects);
        }
        return objects;
    }
}

这是一个解决方案,我希望从java,但它不起作用!我不能像这样...class HandlerChain<T extends MyObjectHandler<V extends MyObject>声明类。为什么我可以在MyObjectHandler之后使用通配符,但不能使用这种结构?

3 个答案:

答案 0 :(得分:4)

解决方案1 ​​

  

这个可行,但在handler.handle(对象)中,我得到未经检查的调用&#39; handle(List)&#39;作为原始类型的成员&#39; MyObjectHandler&#39;,所以我应该添加不太好的@SuppressWarnings(&#34;未经检查&#34;)。

确实,因为MyObjectHandler是通用类型,但您没有在HandlerChain的类型参数中指定它的类型。

解决方案2

  

不起作用。在handler.handle(objects)中,我得到句柄(java.util.List&gt;)不能应用于(java.util.List)。在这种情况下,为什么我无法将对象传递给处理程序?通配符扩展MyObject,V扩展MyObject。还不够吗?

不。 ? extends MyObject基本上是针对延伸MyObject的某种未指定类型表示的,但您并未说明哪种类型。您可以创建一个班级public class BigHandlerChain<BigObjectHandler>,但会向SmallObject提供doChain个实例列表。

解决方案3

  

这个可行,但在这种情况下我应该定义BigHandlerChain,因为类BigHandlerChain扩展了AbstractHandlerChain。但是BigObjectHandler已经包含了可以由它处理的类的信息,因此它的信息重复。

确实,这里有一些重复的信息,但在合成这样的通用类型时,可能会发生这种情况。由于您的doChain方法在某种类型上运行,因此必须指定该类型可以执行的操作。请注意,您将要处理BigObject列表,并且您提供的处理程序必须能够处理BigObjects。

解决方案4

  

这是一个解决方案,我期望从java,但它不起作用!我不能像这样声明类...类HandlerChain。为什么我可以在MyObjectHandler之后使用通配符,但不能使用这种结构?

问题是V表示某种特定类型,而不是通配符,因此您需要指定V是什么。

换句话说,尽管有一些重复的信息,但您的解决方案3是正确的方法。不幸的是,你会在Java中看到更多这样的东西。当字段上的特定修饰符关键字可以实现相同时(如在Ruby中),getter / setter可以说是不必要的样板。

但请注意,如果您指定public abstract class HandlerChain<T extends MyObjectHandler<V>, V extends MyObject>,则指定该链仅适用于某种特定类型的MyObjectHandler。由于我认为您可能需要一系列能够处理相同对象类型的不同处理程序,因此您最好只指定该对象类型:

public abstract class HandlerChain<V extends MyObject> {

    private List<MyObjectHandler<V>> handlers;

    public HandlerChain(List<MyObjectHandler<V>> handlers) {
        this.handlers = handlers;
    }

    public List<V> doChain(List<V> objects) {
        for (MyObjectHandler<V> handler : handlers) {
            objects = handler.handle(objects);
        }
        return objects;
    }

}

答案 1 :(得分:3)

您不需要处理程序是通用的,只需要对象:

public class MyClass: IMyInterface
{
    public int X { get; set; }
    public int Y { get; set; }
    public List<MyClass> MyList { get; set; }
}

这允许:

public abstract class HandlerChain<V extends MyObject> {

    private List<MyObjectHandler<V>> handlers;

    public HandlerChain(List<MyObjectHandler<V>> handlers) {
        this.handlers = handlers;
    }

    public List<V> doChain(List<V> objects) {
        for (MyObjectHandler<V> handler : handlers) {
            objects = handler.handle(objects);
        }
        return objects;
    }

}

编辑:如果你比较G_H’s solution和我的,唯一的差异是G_H使用 new HandlerChain<BigObject>(Arrays.asList(new BigHandler1(), new BigHandler2())) { // ... }; 。这允许您传递一个列表,其中声明的元素类型比List<? extends MyObjectHandler<V>>更具体。我认为你不太可能需要这个,但你也可以采用这种额外的灵活性。

答案 2 :(得分:0)

修复解决方案4很简单,因为:Java编译器希望将通配符一起声明,然后使用:

REPLACE