类继承:泛型扩展泛型

Fee*_*ard 10 java generics inheritance

假设我有简单的课程

public class MyObject {
}
Run Code Online (Sandbox Code Playgroud)

以及处理MyObject子类的处理程序接口

public interface MyObjectHandler<V extends MyObject>{
     List<V> handle(List<V> objects);
}
Run Code Online (Sandbox Code Playgroud)

假设,我有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 {....}
Run Code Online (Sandbox Code Playgroud)

现在让我们假设我们已经创建了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;
    }
} 
Run Code Online (Sandbox Code Playgroud)

问题:是否可以为此案例创建完美的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这个可以工作,但是在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

不行.在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这个可行,但在这种情况下我应该将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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

G_H*_*G_H 4

解决方案1

这个有效,但是在 handler.handle(objects) 中,我得到对“handle(List)”的未经检查的调用,作为原始类型“MyObjectHandler”的成员,所以我应该添加@SuppressWarnings(“unchecked”),这不是很好。

事实上,becauseMyObjectHandler是一个泛型类型,但您没有在HandlerChain的类型参数中指定它的类型。

解决方案2

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

没有。? extends MyObject基本上说它是针对某些未指定的类型扩展的MyObject,但你没有说是哪个。您可以创建一个类,但向 提供实例public class BigHandlerChain<BigObjectHandler>列表。SmallObjectdoChain

解决方案3

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

事实上,这里有一些重复的信息,但是当像这样组合泛型类型时,很可能会发生这种情况。由于您的doChain方法对某种类型进行操作,因此必须指定该类型可以做什么。可以将其视为您想要处理BigObject列表,并且您提供的处理程序必须能够处理 BigObject。

解决方案4

这是我期望从java获得的解决方案,但它不起作用!我无法声明像这样的类...class 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;
    }

}
Run Code Online (Sandbox Code Playgroud)