I have a problem understanding the behaviour of Java generics in the following case.
Having some parametrised interface, IFace<T>
, and a method on some class that returns a class extending this interface, <C extends IFace<?>> Class<C> getClazz()
a java compilation error is produced by gradle
, 1.8 Oracle JDK, OSX and Linux, but not by the Eclipse compiler within the Eclipse IDE (it also happily runs under Eclipse RCP OSGi runtime), for the following implementation:
public class Whatever {
public interface IFace<T> {}
@SuppressWarnings("unchecked")
protected <C extends IFace<?>> Class<C> getClazz() {
return (Class<C>) IFace.class;
}
}
➜ ./gradlew build
:compileJava
/Users/user/src/test/src/main/java/Whatever.java:6: error: incompatible types: Class<IFace> cannot be converted to Class<C>
return (Class<C>) IFace.class;
^
where C is a type-variable:
C extends IFace<?> declared in method <C>getClazz()
1 error
:compileJava FAILED
This implementation is not a very logical one, it is the default one that somebody thought was good, but I would like to understand why it is not compiling rather than question the logic of the code.
The easiest fix was to drop a part of the generic definition in the method signature. The following compiles without issues, but relies on a raw type:
protected Class<? extends IFace> getClazz() {
return IFace.class;
}
Why would this compile and the above not? Is there a way to avoid using the raw type?