I found a strange inconsistence when implementing a Java interface that makes heavy use of generics and I can't find an explanation to why this happens.
I've stripped down the example as much as possible and I'm aware that the usage of generics makes not much sense in this example anymore.
public interface Service<T> {
public List<T> thisCompiles();
public List<T> andThisCompiles(List<Object> inputParam);
public <S extends T> List<S> thisCompilesAswell();
public <S extends T> List<S> evenThisCompiles(List inputParam);
public <S extends T> List<S> butThisDoesnt(List<Object> inputParam);
}
public class ServiceImpl implements Service<Number> {
@Override
public List<Number> thisCompiles() {
return null;
}
@Override
public List<Number> andThisCompiles(List<Object> inputParam) {
return null;
}
@Override
public List<Number> thisCompilesAswell() {
return null;
}
@Override
public List<Number> evenThisCompiles(List inputParam) {
return null;
}
@Override
public List<Number> butThisDoesnt(List<Object> inputParam) {
return null;
}
}
Please note that in the implementation, all return types are List<Number>
, although the interface is more generous.
When compiling this snippet (I tried Oracle JDK 8 u144 and OpenJDK 8 u121), I get the following error messages:
- The method
butThisDoesnt(List<Object>)
of typeServiceImpl
must override or implement a supertype method - The type
ServiceImpl
must implement the inherited abstract methodService<Number>.butThisDoesnt(List<Object>)
- Name clash: The method
butThisDoesnt(List<Object>)
of typeServiceImpl
has the same erasure asbutThisDoesnt(List<Object>)
of typeService<T>
but does not override it
It seems that compilation fails as soon as the parameter that I pass to the function has some generic parameter itself.
Implementing the 5th function as
@Override
public <S extends Number> List<S> butThisDoesnt(List<Object> inputParam) {
return null;
}
works as expected.
Is there an explanation for this sort of behaviour or did I stumble upon a bug (although it happens in OpenJDK aswell)? Why is the 5th function behaving differently?
I am not interested in finding out how I can make this code compile (I fixed it for my codebase). I just stumbled upon this problem and am curious about the logical explanation why exactly the compilers accept the first four methods but reject the fifth.
List– matt Jul 27 '17 at 14:39` is different than `List` so the first version doesn't override the abstract method. So it is clear you haven't implemented all of the methods. It is also clear you haven't actually overridden anything, so your `@Override` is borked. Finally you have a name clash because `method(List a)` is the same signature as `method(List b)` as explained in the answer below. List` in the interface, but they compile. The name clash is only a result of the `@Override` not working.List– matt Jul 27 '17 at 15:03thisCompilesAswell();` and it still compiles, but you can subvert type safety.` (if compiled with `Xlint:unchecked`). Still, I think you miss the point of my question - I want to know, why the methods behave differently when it comes to compiling. This example is not about possible runtime exceptions, about how much sense the generics make or if there would be a better way to program the interface.