Suppose I have an interface like so
public interface Foo<Ret, Arg> {
public Ret doSomething(Arg arg);
}
And a concrete implementing class that only has package visibility.
class FooImpl<Ret, Arg1, Arg2> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Arg2> {
// This class also implements something else making use of Arg2, so you cannot safely remove it
// But nothing relating to Arg2 is needed to create a FooImpl
}
(An example of how this might happen is the implementation of Foo and Bar in FooImpl forwards methods declared by the two interfaces to one varargs method)
Now, suppose there is a static method somewhere in the same package that returns a FooImpl as a Foo. One would think that you could use return new FooImpl<Ret, Arg1, ?>(...), when in fact you can’t (you have to use a concrete dummy type as Arg2, i.e.. return new FooImpl<Ret, Arg1, Object>(...), say).
Any idea why this is, especially as the fact that the Foo interface and the package visibility effectively hides the FooImpl from whatever is using the static method? Is it because of the fact that one could still use reflection to some extent in order to get to the Bar parts of FooImpl, where a concrete type is needed?
The Java compiler tries hard not to be smart. There are many situations when it is obvious that it doesn’t need some information to produce correct code but it still complains. When Java was invented, the world had seen C with it’s sloppy attitude to try to compile everything, no matter how dangerous or stupid. The goal for
javacwas to make sure people can’t shoot themselves into the foot easily.Therefore, it assumes that there is a reason that you mention
Arg2– if it wasn’t necessary for something, you surely wouldn’t have put it into the code.Solutions:
Bar:class FooImpl<Ret, Arg1> implements Foo<Ret, Arg>, Bar<Ret, Arg1, Object>BetterFooImpl<Ret, Arg1> extends FooImpl<Ret, Arg1, Object>. That hides the third type argument from consumers of the API.Bar@SuppressWarningannotations. When this happens, I ask on SO and when I can’t find a solution that I can understand, I remove the generics. It’s more important to write maintainable code than finding clever ways through the generics maze.