I have two different interfaces
interface ColumnSet {
<V, I extends Column & Input<V>> V getValue(I column);
}
interface ParameterSet {
<V, I extends Parameter & Input<V>> V getValue(I value);
}
where the types Column
and Parameter
are simply marker interfaces to stop Column
s being used where Parameter
s should be and vice-versa. Behind the scenes I would therefore like to have a single class that implements them both as follows:
class ObjectSet implements ColumnSet, ParameterSet {
@Override public <V, I extends Input<V>> V getValue(I input) {
...
}
}
Logically it seems like ObjectSet.getValue
should be a valid override for both ColumnSet.getValue
and ParameterSet.getValue
as it takes any Input<V>
as an argument which is upper bounded by both Column & Input<V>
and Parameter & Input<V>
. However Java 9 doesn't recognise it as overriding either of them reporting The method getValue() of type ObjectSet must override or implement a generic supertype method
.
Is this a limitation of generics in Java or am I missing something fundamental?
(Obviously I can't create two separate methods in ObjectSet
due to them having the same erasure, which leaves me with the alternative of giving different names for the two getValue
methods in the interfaces which I'm trying to avoid).
According to (§8.4.8.1), an instance method m1
overrides another instance method m2
if the signature of m1
is a subsignature (§8.4.2) of the signature of m2
.
In other words, the overriding method signature should be the same as either overridden method signature or erasure (§4.6) of the overridden method signature.
So overridden method parameters can't be replaced with parameters of a less specific type in the overriding method. You can read why this is so here.
In your case, the ObjectSet#getValue
signature isn't the same as the ColumnSet#getValue
signature and its erasure signature (Object getValue(Column column)
). The same goes for ParameterSet#getValue
.
As it was pointed out by @samabcde if base methods are declared like this:
<V, I extends Column & Input<V>> V getValue(I column);
<V, I extends Parameter & Input<V>> V getValue(I value);
You can implement them as follows:
@Override
public <V, I extends Column & Input<V>> V getValue(I value) {
return doGetValue(value);
}
@Override
public <V, I extends Parameter & Input<V>> V getValue(I value) {
return doGetValue(value);
}
private <V, I extends Input<V>> V doGetValue(I value) { ... }
And if base methods are declared like this:
<V, I extends Input<V> & Column> V getValue(I value);
<V, I extends Input<V> & Parameter> V getValue(I value);
You can only implement their erasure as follows:
@Override
public Object getValue(Input value) { ... }
Since neither of these options looks good I recommend redesigning your code the following way:
public interface Input<R, V> {
// ...
}
public interface ObjectSet<R> {
<V> V getValue(Input<R, V> input);
}
public class ObjectSetImpl<R> implements ObjectSet<R> {
@Override
public <V> V getValue(Input<R, V> input) {
// ...
}
}
Now you can easily create ObjectSet
instances that only accept Input
with specific type parameter R
:
public interface Column<V> extends Input<Column<?>, V> {}
ObjectSet<Column<?>> columnSet = new ObjectSetImpl<>();
If you dislike writing ObjectSet<Column<?>>
, you can create a better-named implementation of ObjectSet<Column<?>>
that delegates all the work to ObjectSetImpl
:
public class ColumnSet extends DelegatingObjectSet<Column<?>> {}
ColumnSet columnSet = new ColumnSet();
Where DelegatingObjectSet
is:
abstract class DelegatingObjectSet<R> implements ObjectSet<R> {
// you can use dependency injection here
private final ObjectSet<R> delegate = new ObjectSetImpl<>();
@Override
public <V> V getValue(Input<R, V> input) {
return delegate.getValue(input);
}
}