Given the following interface,
public interface Callback<T> {
<K, V> T execute(Operation<K, V> operation) throws SomeException;
}
How would I implement the interface in a new anonymous class where operation
is of type Operation<String,String>
e.g, this doesn't compile:
Callback<Boolean> callback = new Callback<Boolean>() {
@Override
public Boolean execute(Operation<String, String> operation) {
return true;
}
};
executor.execute(callback);
the generics on the method are unrelated to the class generic parameters
you need to repeat them on the method for it to be correct, e.g.
Callback<Boolean> callback = new Callback<Boolean>() {
@Override
public <X,Y> Boolean execute(Operation<X, Y> operation) {
}
};
executor.execute(callback);
In other words the interface requires an execute method that works on any Operation parameters.
If you want a callback that only works on specific parameters you need to make them part of the class signature, e.g.
public interface Callback<T,K,V> {
T execute(Operation<K, V> operation) throws SomeException;
}
that would then let you do
Callback<Boolean,String,String> callback = new Callback<Boolean,String,String>() {
@Override
public Boolean execute(Operation<String, String> operation) {
}
};
executor.execute(callback);
I cannot see a route to getting what you want... unless you start using the <? super K,? super V>
or <? extends K,? extends V>
forms which may restrict you too much.
Here is what your interface erases to
public interface Callback<T> {
T execute(Operation<Object, Object> operation) throws SomeException;
}
then when you instantiate with T == Boolean
we get
public interface Callback {
Boolean execute(Operation<Object, Object> operation) throws SomeException;
}
which cannot be implemented by a
Boolean execute(Operation<String, String> operation) throws SomeException;
method as the in parameters are narrower. You can widen in parameters and narrow out parameters but you cannot go the other way.
That explains why you can change the return type (out parameter) from Object
to Boolean
as anyone expecting an Object
will be happy with a Boolean
.
Conversely we cannot widen the return type as that would give a ClassCastException
to anyone calling the method and acting on the result.
The method arguments (in parameters) can only be widened. Now it is somewhat complex for method arguments as Java sees different types as different methods, so you can legally have
public interface Callback<T> {
T execute(Object key, Object value);
}
Callback<Boolean> cb = new Callback<Boolean> {
@Override
public Boolean execute(Object k, Object v) { ... }
// not an @Override
public Boolean execute(String k, String v) { ... }
}
because the second method has a different signature. But your Operation<X,Y>
class gets erased to just the raw type irrespective of whether it is an Operation<String,String>
or Operation<X,Y>
There is one thing you could do... but it gets messy!
public interface StringOperation extends Operation<String,String> {}
then you can do
Callback<Boolean> cb = new Callback<Boolean> {
@Override
public <K,V> Boolean execute(Operation<K,V> o) { ... }
// not an @Override
public Boolean execute(StringOperation o) { ... }
}
but keep in mind that the execute(Callback<?>)
method will be calling <K,V> Boolean execute(Operation<K,V> o)
and not Boolean execute(StringOperation o)
Because the type parameter in your class name and the type parameter in the method are actually different You can do below.
@Override
public <K, V> Boolean execute(Operation<K, V> operation)
throws SomeException {
return false;
}