Why is isAnnotationPresent works differently between Java 7 and Java 8?

I just discovered this today when one of my unit tests failed due to an upgrade from Java 7 to Java 8. unit test calls a method that tries to find an annotation of a method that is annotated on a child class, but with a different return type.

In Java 7, isAnnotationPresent seems to only find annotations if they were indeed declared in code. In Java 8, isAnnotationPresent seems to contain annotations that were declared in child classes.

To illustrate this, I created a simple (??) test class IAPTest (for IsAnnotationPresentTest).

 import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; public class IAPTest { @Retention(RetentionPolicy.RUNTIME) public static @interface Anno { } public static interface I { } public static interface IE extends I { } public static class A { protected I method() { return null; } } public static class B extends A { @Anno protected IE method() { return null; } } public static void main(String[] args) { for (Method method : B.class.getDeclaredMethods()) { if (method.getName().equals("method") && I.class.equals(method.getReturnType())) { System.out.println(method.isAnnotationPresent(Anno.class)); } } } } 

In the latest version of Java 7 (1.7.0_79 at the time of writing), this method prints "false". In the latest Java 8 (1.8.0_66 at the time of writing), this method prints "true". I would intuitively expect it to print "false".

Why is this? Does this mean a bug in Java or an alleged change in Java?

EDIT : just to show the exact commands I used for replication (in a directory with IAPTest.java identical to the code block above):

 C:\test-isannotationpresent>del *.class C:\test-isannotationpresent>set JAVA_HOME=C:\nma\Toolsets\AJB1\OracleJDK\jdk1.8.0_66 C:\test-isannotationpresent>set PATH=%PATH%;C:\nma\Toolsets\AJB1\OracleJDK\jdk1.8.0_66\bin C:\test-isannotationpresent>java -version java version "1.8.0_66" Java(TM) SE Runtime Environment (build 1.8.0_66-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode) C:\test-isannotationpresent>javac IAPTest.java C:\test-isannotationpresent>java IAPTest true C:\test-isannotationpresent> 
+6
source share
1 answer

I believe this is due to the change mentioned in the java 8 compatibility guide

Starting with this version, annotations of parameters and methods are copied to synthetic bridge methods. This fix implies that now for programs such as:

 @Target(value = {ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @interface ParamAnnotation {} @Target(value = {ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MethodAnnotation {} abstract class T<A,B> { B m(A a){ return null; } } class CovariantReturnType extends T<Integer, Integer> { @MethodAnnotation Integer m(@ParamAnnotation Integer i) { return i; } public class VisibilityChange extends CovariantReturnType {} } 

Each bridge method created will have all the method annotations to which it is redirected. Parameter annotations will also be copied. This change in behavior may affect some annotation processors, or in general any application using annotations.

The second method, which returns I instead of IE , is a synthetic method created because you have a narrower return type in the overridden method than in the superclass. Note that it is not in the list of declared methods unless you have a restriction on the return type. Therefore, I think this is not a mistake, but a deliberate change.

+10
source

All Articles