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?
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.
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?
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.
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?
A generic class can extend only one generic class and one or more generic interfaces. Then it's type parameters should be union of type parameters of generic class and generic interface(s).
Generic Classes and SubtypingYou can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses.
Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.
A Generic class can have muliple type parameters.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With