Why these generics do not compile in OpenJDK7, but do it in OpenJDK6

class HasId<I> {} class HasStringId extends HasId<String> {} class Alert<T extends /*Some*/Object> extends HasStringId {} class BaseController<M extends HasId<String>> { // abstract Class<M> getModelClass(); } class AlertController extends BaseController<Alert> { // error here // @Override Class<Alert> getModelClass() { // return Alert.class; // } } 

compiles fine on OpenJDK6, but in OpenJDK7 it gives:

 AlertController.java:50: error: type argument Alert is not within bounds of type-variable T class AlertController extends BaseController<Alert> { ^ where T is a type-variable: T extends HasId<String> declared in class BaseController 

Note that line 50 indicates the rawtype warning, because the Alert parameter must be parameterized. If I do this, for example. extends BaseController<Alert<Object>> , compiled code. But I cannot do this because I need to implement getModelClass ().

UPDATE: this was a bug in Java 6 implementations that was fixed in Java 7: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182 . (And here is my question for the compiler developers: http://openjdk.5641.n7.nabble.com/Nested-generics-don-t-compile-in-1-7-0-15-but-do-in-1 -6-0-27-td121820.html )

+8
java generics openjdk
source share
3 answers

The question is whether HasId<String> supertype of the original Alert type. The specification is not very clear on this issue.

In the spirit of [4.8], rawtype supertypes must also be erased. Therefore, Alert must have a supertype HasId , but not HasId<String> . However, the section speaks only in terms of “superclasses / interfaces”, and not in terms of “supertypes”.

In the spirit of [4.10], supertypes are detected through direct supertypes. It is unclear how the section relates to raw types. He probably suggests that raw Alert has a direct supertype of HasStringId . That seems fair. Then, since HasId<String> is a direct supertype of HasStringId , by transitivity, HasId<String> is a supertype Alert !

The confusion is rooted in the fact that there are actually two types of HasStringId , one normal, one raw. Although HasStringId not common for itself, it has a common supertype, so it makes sense to talk about an unprocessed version of HasStringId .

The specification makes no distinction between regular and raw HasStringId . This is an oversight.

Suppose raw HasStringId referred to as HasStringId' , then [4.10] makes more sense. The raw Alert direct super HasStringId' is raw HasStringId' . HasStringId' direct super-interface is raw HasId . Therefore, HasId is a supertype of Alert , not HasId<String> .

See section 4 of the JLS . I am linking to the JLS preview here, as JLS 7 has serious editing errors in section 4.10.2.

+2
source share

There are several documented cases of Java 7 compilers that are more stringent than Java 6 compilers for the various nuances of generics. These cases are often related to the actual specification of the language, becoming more specific. The error is probably due to the use of some unprocessed type, which essentially "refused" generics in inherited types - whether it is debatable correctly.

EDIT: I could not find this problem in the JDK 7 incompatibility list . The error is reproduced using sun-jdk-1.7.0_10 , but not with the Eclipse compiler (which historically has a much better track record than javac when it comes to generic nuances). You must send an Oracle error .

Here's a workaround possible:

 class AlertController extends BaseController<Alert<?>> { @Override @SuppressWarnings("unchecked") Class<Alert<?>> getModelClass() { return (Class<Alert<?>>)(Class<?>)Alert.class; } } 
+1
source share

I believe this is due to how the erasure is handled in the absence of actual type parameters. When a parameterized type is referenced without any type parameters, all references to these parameters are erased.

In this case, you have a parameterized type Alert , which is used without any type parameter. This erases all type parameters on Alert and its superclasses. This HasId parameter of type HasId in the extends HasStringId . Alert , then not a subclass of HasId<String> , because HasStringId no longer extends it, but extends HasId .

The Paul B. workaround or one below avoids this problem by always using Alert with parameters of its type.

 class AlertController<T> extends BaseController<Alert<T>> { @Override Class<Alert<T>> getModelClass() { return cast(Alert.class); } @SuppressWarnings("unchecked") private <T> T cast(final Object o) { return (T) o; } } 
+1
source share

All Articles