2015-11-05 34 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

相關問題