为什么isAnnotationPresent在Java 7和Java 8之间的工作方式不同?

我刚刚发现这个,因为我的一个unit testing由于从Java 7升级到Java 8而失败。unit testing调用一个方法,该方法试图在子类上注释但具有不同返回类型的方法上查找注释。

在Java 7中, isAnnotationPresent似乎只能在代码中真正声明它们时才能找到注释。 在Java 8中, isAnnotationPresent似乎包含在子类中声明的注释。

为了说明这一点,我创建了一个简单的(??)测试类IAPTest(用于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)); } } } } 

在最新的Java 7(撰写本文时为1.7.0_79),此方法打印“false”。 在最新的Java 8(写作时为1.8.0_66),此方法打印“true”。 我会直觉地期望它打印“假”。

为什么是这样? 这是否表明Java中的错误或Java的工作方式有何变化?

编辑 :只是为了显示我用来复制它的确切命令(在IAPTest.java与上面的代码块相同的目录中):

 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> 

我相信这与java 8兼容性指南中提到的更改有关

在此版本中,参数和方法注释被复制到合成桥接方法。此修复意味着现在对于以下程序:

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

每个生成的桥接方法都将具有重定向到的方法的所有注释。 参数注释也将被复制。 行为的这种改变可能会影响某些注释处理器或一般任何使用注释的应用程序。

返回I而不是IE的第二种方法是生成的合成方法,因为重写方法中的返回类型比超类中的返回类型更窄。 请注意,如果您没有缩小返回类型,则它不会出现在声明的方法列表中。 所以我认为这不是一个错误,而是一个故意的改变。