2015-11-05 33 views
6

我刚刚发现了这个,因为从Java 7升级到Java 8时,我的一个单元测试失败了。单元测试调用了一种方法,该方法尝试在子类上注释的方法上尝试查找注释,但使用不同的方法返回类型。为什么isAnnotationPresent在Java 7和Java 8之间的工作方式不同?

在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在写作时),这种方法打印 “假”。在最新的Java 8(写作时为1.8.0_66)中,此方法打印出“true”。我会直觉地期望它打印“错误”。

这是为什么?这是否表明Java中存在错误或者Java如何工作?

EDIT:只是为了示出的确切命令我用来复制本(与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> 
+0

嗯,这为我打印'false':Eclipse Mars 4.5.1,JDK 1.8.0_51。 – Tunaki

+0

你为什么认为注释“是在儿童课堂上申报的”?你在'B'中注解了方法,并且你正在搜索'B'的*声明的方法*,而没有其他的东西。没有涉及的子类。 – Holger

+0

@Holger当我搜索'B'的声明方法时,我发现了两种方法。一个方法表示子类的方法,一个方法表示父类的方法。你可以从返回类型中看出来。 'B.class.getDeclaredMethods()。length == 2'在Java 7和8上。因为我正在检查返回类型以确保父类方法是被引用的方法(返回类型是'I'而不是'IE'),'A'中的方法没有注解,但只有子类“B”中的注释。 – Kidburla

回答

10

相信这是变化相关的在java 8 compatibility guide

作为该释放的所提到的,参数和方法的注释被复制到 合成桥methods.This固定意味着现在这样的程序:

@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 {} 
} 

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

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

+1

很好的回答!您不仅向我提供了推理和文档引用,还提供了解决单元测试问题以处理Java 8的方法 - 我只需要使用'isSynthetic'。谢谢! – Kidburla

相关问题