Class inheritance: generic extends generic

Feedforward :

Let's say I have simple class

public class MyObject {
}

And the handler interface for processing the child classes of MyObject

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

Suppose, I have BigObjects and SmallObjects (both of them extends MyObject) and I want to have separate handlers for them. So, I create two intefaces of MyObjectHandler with specific generics.

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 {....}

Now let's imagine that we have created AbstractHandlerChain<...> abstract class. So, we can create BigHandlerChain class and inject our BigHandlers (and the same thing with SmallHandlerChain).

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

The question: is it possible to create perfect AbstractHandlerChain for this case?

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

This one works, but in handler.handle(objects) I get Unchecked call to 'handle(List<V>)' as a member of raw type 'MyObjectHandler', so I should add @SuppressWarnings("unchecked") that is not very good.

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

Doesn't work. In handler.handle(objects) I get handle (java.util.List<capture<? extends MyObject>>) cannot be applied to (java.util.List<V>). Why I can't pass objects to handlers in this case? Wildcard extends MyObject and V extends MyObject. Isn't it enought?

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

This one works, but in this case I should define BigHandlerChain as class BigHandlerChain extends AbstractHandlerChain<BigObjectHandler, BigObject>. But BigObjectHandler already contains information about classes, that can be handled by it, so it's information duplication.

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

Here is a solution, that I expect from java, but it doesn't work! I can't declare class like this ...class HandlerChain<T extends MyObjectHandler<V extends MyObject>. Why I can use wildcards after MyObjectHandler, but can't use this construction?

G_H :

Solution 1

This one works, but in handler.handle(objects) I get Unchecked call to 'handle(List)' as a member of raw type 'MyObjectHandler', so I should add @SuppressWarnings("unchecked") that is not very good.

Indeed, because MyObjectHandler is a generic type but you don't specify the type for it in HandlerChain's type parameter.

Solution 2

Doesn't work. In handler.handle(objects) I get handle (java.util.List>) cannot be applied to (java.util.List). Why I can't pass objects to handlers in this case? Wildcard extends MyObject and V extends MyObject. Isn't it enough?

Nope. ? extends MyObject basically says it's for some unspecified type extending MyObject, but you're not saying which. You could create a class public class BigHandlerChain<BigObjectHandler> but supply a list of SmallObject instances to doChain.

Solution 3

This one works, but in this case I should define BigHandlerChain as class BigHandlerChain extends AbstractHandlerChain. But BigObjectHandler already contains information about classes, that can be handled by it, so it's information duplication.

Indeed, there's some duplicate information here, but when compositing generic types like this, that's likely to happen. Since your doChain method operates on some type, it must be specified what that type can do. See it as saying you'll want to handle BigObject lists and the handlers you provide must be able to handle BigObjects.

Solution 4

Here is a solution, that I expect from java, but it doesn't work! I can't declare class like this ...class HandlerChain. Why I can use wildcards after MyObjectHandler, but can't use this construction?

The problem is that V indicates some specific type, not a wildcard, so you'll need to specify what V is.

In other words, your solution 3 is the correct approach, despite some duplicate information. Unfortunately, you're gonna see more of this in Java. Getters/setters are arguably unnecessary boilerplate when specific modifier keywords on the fields could achieve the same (like in Ruby).

Note however that if you specify public abstract class HandlerChain<T extends MyObjectHandler<V>, V extends MyObject>, you're specifying the chain as only being suitable for one specific type of MyObjectHandler. Since I'm thinking you'll probably need a chain of different handlers that are all capable of handling the same object types, you're better off only specifying that object type:

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

}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=438328&siteId=1