I have couple of supplied interfaces
public interface Finder<S extends Stack<T>,T extends Item> {
public S find(S s, int a);
}
public interface Stack<T extends Item> {
Stack<T> getCopy();
}
and a class that implements the first:
public class SimpleFinder<S extends Stack<T>,T extends Item> implements Finder<S,T>{
public S find(S s, int a){
S stack = ....;
...
stack = s.getCopy(); \\Error: incompatible types
\\ required: S
\\ found: Stack<T>
.....
return stack;
}
}
If I cannot change any interface what would be the best course of action while keeping the implementation as generic as possible?
EDIT
Some other code which I cannot break instantiates SimpleFinder<ClassA,ClassB>
so I should have two generic types in the implementation as well.
The problem is that obviously Stack<T>
is not S extends Stack<T>
. Java is strongly typed and won't let you do such things.
You can either cast to Stack<T>
, in which case you will still get a warning about unchecked conversion. This means this conversion is unsafe.
public class SimpleFinder<S extends Stack<T>, T extends Item> implements Finder<S, T> {
@Override
public S find(S s, int a) {
Stack<T> stack = s.getCopy();
return (S) stack;
}
}
or simply use Stack<T>
instead of S extends Stack<T>
, which is my recommendation:
public class SimpleFinder<T extends Item> implements Finder<Stack<T>, T> {
@Override
public Stack<T> find(Stack<T> s, int a) {
Stack<T> stack = s.getCopy();
return stack;
}
}
Since you can't change the interfaces, you have no choice but to do brute cast.
In a more general discussion, what we need here is "self type", we want to say that a method invocation foo.bar()
should return the static type of foo
. Usually self type is wanted for fluent API where the method should return foo
itself. In your case you want to return a new object.
In java there's no satisfactory answer for self type. One trick is through self referenced type paramter like Foo<T extends Foo<T>>
, however it is very ugly, and it cannot really enforce that any subtype Bar
must be a Foo<Bar>
. And the trick won't help in your case at all.
Another trick may work
public interface Stack<T extends Item> {
<X extends Stack<T>> X getCopy();
}
here, the caller supplies the exact return type.
S stack = ....;
...
stack = s.getCopy();
// compiles, because X is inferred to be S
This trick helps to simplify call sites. However brute casts still exists, hidden in implementations of getCopy()
. This trick is dangerous and caller must know what it is doing. Personally I wouldn't do it; it's better for force caller to do the cast.
As discussed in the comments, your design necessitates that the getCopy
method return the "self type" - that is, a BlueStack<T>
implementation would be expected to return a BlueStack<T>
from its getCopy
, and RedStack<T>
should return a RedStack<T>
etc.
Unfortunately, there is no way to express the "self type" in Java. As zhong.j.yu points out, a recursive type parameter comes close, for example:
//not recommended!
public interface Stack<S extends Stack<S, T>, T extends Item> {
S getCopy();
}
But as zhong.j.yu mentions this is unintuitive and would still fail to prevent a BlueStack<T>
from "lying" and returning a RedStack<T>
from its getCopy
.
Instead, I recommend a redesign. Try decoupling the responsibility of copying stacks from the Stack
type itself. For example:
public interface StackCopier<S extends Stack<T>, T extends Item> {
S copy(S original);
}
If implementations of StackCopier
need access to private members of their respective Stack
s, consider making them nested classes, for example:
class BlueStack<T extends Item> implements Stack<T> {
...
static class Copier<T extends Item> implements StackCopier<BlueStack<T>, T> {
@Override
public BlueStack<T> copy(BlueStack<T> original) {
...
}
}
Of course SimpleFinder
would need to be changed to either have a StackCopier<S, T>
field or take one as a new parameter of find
:
private final StackCopier<S, T> copier = ...;
public S find(S stack, int a) {
S stackCopy = copier.copy(stack);
...
return stackCopy;
}
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