I am having trouble building a somewhat circular generic configuration. I need a "state" interface that provides a getter to a generic "container" interface. The container interface on its methods needs to accept a generic "state" as one of the parameters. I have tried various options (truly circular generic class parameters, various variations of below), but what I have below is the closest to what I think I need:
interface Container<K> {
<C extends Container<K>,S extends State<K,C>> C setData(K key, Object val,S state);
}
interface State<K,C extends Container<K>>{
C getContainer();
}
class BasicContainer<K> implements Container<K> {
public <C extends BasicContainer<K>, S extends State<K,C>> C setData(K key, Object val, S state) { return this;}
}
class BasicState<K> implements State<K,BasicContainer<K>> {
BasicContainer<K> container = new BasicContainer<K>();
public BasicContainer<K> getContainer(){
return container;
}
}
Alas, the compiler is giving the famous methods have same erasure yet neither overrides the other
for the BasicContainer
method. I believe this is because while C extends BasicContainer<K>
is a subtype of C extends Container<K>
, S extends State<K,C>
is not a subtype of S extends State<K,C>
in Container<K>
.
Any suggestions on how to accomplish by desired configuration?
Update
I will need other implementations of Container
that also need to work with a State
implementation, but the State
implementation will not return those implementations. This is where the circular parameters on a class failed.
It looks like C
ought to be a type parameter on the Container
interface, like K
.
In general, if Foo
is a supertype of Bar
, Bar
cannot add additional constraints to generic type parameters on methods from Foo
. This just follows from the Liskov substitution principle: any Bar
must be able to be used for anything a Foo
can be used for.
Constraining C extends BasicContainer<K>
in the type parameters of BasicContainer.setData
, when Container.setData
only has the constraint C extends Container<K>
, specifically violates this rule.
What you can do instead is have the upper bound on C
as a type parameter on Container,
and set it to a different value in BasicContainer
.
This could look like one of the following:
interface Container<K, C extends Container<K, C>> {
<S extends State<K,C>> C setData(K key, Object val,S state);
}
class BasicContainer<K> implements Container<K, BasicContainer<K>> { ... }
...or, if you find you need the additional generics,
interface Container<K, C extends Container<K, C>> {
<C2 extends C, S extends State<K,C>> C2 setData(K key, Object val,S state);
}
class BasicContainer<K> implements Container<K, BasicContainer<K>> { ... }
(though bluntly, the second version is almost certainly not what you want).