It was pointed out already that there is neither a solution nor a nice workaround. Here is what I finally did. It only works for my special case, but you can take it as an inspiration if you run into similar problems. (It also explains why I ran into this problem)
First of all, there is this class (showing only the relevant interface):
class Pipe<Input, Output> {
boolean hasNext();
Input getNext();
void setNext(Output o);
}
The Foo
interface is actually
interface Processor<Input, Output> {
process(Pipe<Input, Output> p);
}
and the class Bar
should work like this
class JustCopyIt<Input, Output> implements Processor<Input, Output> {
process(Pipe<Input, Output> p) {
while (p.hasNext()) p.setNext(p.getNext());
}
}
The easiest way would be to cast the values like this: p.setNext((Output) p.getNext())
.
But this is bad as it would allow to create an instance of JustCopyIt<Integer, String>
. Calling this object would mysteriously fail at some point, but not at the point where the actual error is made.
Doing class JustCopyIt<Type> implements Processor<Type, Type>
would also not work here, because then I am not able to process a Pipe<String, Object>
.
So what I finally did was to change the interface to this:
interface Processor<Input, Output> {
process(Pipe<? extends Input, ? super Output> p);
}
This way, a JustCopyIt<List>
is able to process a Pipe<ArrayList, Collection>
.
While this technically seems to be the only valid solution, it is still bad because it 1) only works for this special case, 2) required me to change the interface (which is not always possible) and 3) made the code of the other processors ugly.
Edit:
Reading Keiths answer again inspired me for another solution:
public abstract class Bar<A, B> implements Foo<A, B> {
public static <B, A extends B> Bar<A, B> newInstance() {
return new BarImpl<B, A>();
}
private static class BarImpl<B, A extends B> extends Bar<A, B> {
// code goes here
}
}
// clean code without visible reversed parameters
Bar<Integer, Object> bar1 = Bar.newInstance();
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error