Java 8 註解探秘

專注的阿熊發表於2019-11-01

Java SE 1.5引入了註解,程式設計師透過註解可以為程式編寫後設資料(metadata)。根據 Oracle 官方文件,註解的定義如下:“ 註解是後設資料的一種形式,提供與程式相關的資料,該資料不是程式本身的一部分”。 

可以在程式碼中的任何位置使用註解,比如類、方法和變數上使用。從 Java 8開始,註解也可以用於型別宣告。

註解程式碼與程式本身沒有任何直接關係,只有其他程式或 JVM 利用註解資訊實現特定功能。

註解語法

註解由字元 @ 和註解名組成,即 @AnnotationName。當編譯器發現這個語法該元素時,會把它解析為註解。例如:

@ExampleAnnotation

public class SampleClass {
}

上面的註解稱為 ExampleAnnotation,標記在 SampleClass 上。

註解可以包含屬性,在宣告註解時以鍵值對的形式給出。例如: 

@ExampleAnnotation(name = ”first name”, age = 
35)

public void simpleMethod () {
}

請注意,這裡 ExampleAnnotation 是一個方法註解。如果註解只包含一個屬性,在宣告註解時可以忽略屬性名。示例如下:

@ExampleAnnotation(“I am the only property”)

public void simpleMethod () {
}

一個元素可以使用多個註解。比如下面這個示例:

@Annotation1

@Annotation2(“Another Annotation”)
public class SimpleClass {
}

從 J2SE 8 開始,可以為同一個元素多次使用相同的註解,例如:

@ExampleAnnotation(“Annotation used”)

@ExampleAnnotation(“Annotation repeated”)
public class SimpleClass {
}

在 @Repeatable 部分會對此進行詳細地討論。

Java 預定義註解

Java 支援一組預先定義好的註解。下面介紹了Java Core 中提供的註解:

@Retention:   該註解用來修飾其他註解,並標明被修飾註解的作用域。其 value 的屬性值包含3種:

  • SOURCE:註解僅在原始碼中可用。編譯器和 JVM 會忽略此註解,因此在執行時不可用;

  • CLASS:編譯器會處理該註解,但 JVM 不會處理,因此在執行時不可用;

  • RUNTIME:JVM 會處理該註解,可以在執行時使用。

@Targe t:  該註解標記可以應用的目標元素:

  • ANNOTATION_TYPE:可修飾其他註解;

  • CONSTRUCTOR:可以修飾建構函式;

  • FIELD:可以修飾欄位或屬性;

  • LOCAL_VARIABLE:可以修飾區域性變數;

  • METHOD:可以修飾 method;

  • PACKAGE:可以修飾 package 宣告;

  • PARAMETER:可以修飾方法引數;

  • TYPE:可以修飾 Class、Interface、Annotation 或 enum 宣告;

  • PACKAGE:可以修飾 package 宣告;

  • TYPE_PARAMETER:可以修飾引數宣告;

  • TYPE_USE:可以修飾任何型別。

@Documented:   該註解可以修飾其他註解,表示將使用 Javadoc 記錄被註解的元素。

@Inherited:   預設情況下,註解不會被子類繼承。但是,如果把註解標記為 @Inherited,那麼使用註解修飾 class 時,子類也會繼承該註解。該註解僅適用於 class。注意:使用該註解修飾介面時,實現類不會繼承該註解。

@Deprecated:   標明不應該使用帶此註解的元素。使用這個註解,編譯器會對應生成告警。該註解可以應用於 method、class 和欄位。

@SuppressWarnings:  告訴編譯器由於特定原因不產生告警。

@Override:   該註解通知編譯器,該元素正在覆蓋(Override)父類中的元素。覆蓋元素時,不強制要求加上該註解。但是當覆蓋沒有正確完成時,例如子類方法的引數與父類引數不同或者返回型別不匹配時,可以幫助編譯器生成錯誤。

@SafeVarargs:   該註解斷言(Assert)方法或建構函式程式碼不會對其引數執行不安全(Unsafe)操作。

@Repeatable 註解

該註解表示可以對同一個元素多次使用相同的註解。

下面這個例子有助於更清楚地理解。

首先,需要定義一個可重複修飾 class 的註解。

@Retention (RetentionPolicy.RUNTIME)

@Target (ElementType.TYPE_USE)
@Repeatable (RepeatableAnnotationContainer. class)
public @ interface RepeatableAnnotation () {
    String values ();
}

RepeatableAnnotation可以重複修飾元素。

接下來,定義 RepeatableAnnotationContainer註解型別。這是一個註解型別容器,包含一個 RepeatableAnnotation 型別的陣列。


public @interface RepeatableAnnotationContainer {

   RepeatableAnnotation [] value();
}

現在,Repeatable 可以元素進行多次註釋。



@RepeatableAnnotation (“I am annotating the 
class

”)

@RepeatableAnnotation (“I am annotating the
class again”)
@RepeatableAnnotation (“I am annotating the
class for the third time”)
public class RepeatedAnnotationExample { function(){   //外匯跟單 }

在程式中要獲取註解中的值,先要獲取容器中的陣列,陣列的每個元素將包含一個值。例如:

@RepeatableAnnotation (“I am annotating the 
class”)

@RepeatableAnnotation (“I am annotating the class again”)
@RepeatableAnnotation(“I am annotating the class for the third time”)
public class RepeatableAnnotationExample {
    public static void main (String [] args) {
       Class object = RepeatableAnnotationExample. class
       Annotation[] annotations = object.getAnnotations();
for (Annotation annotation : annotations) {
   RepeatableAnnotationContainer rac = (RepeatableAnnotationContainer) annotation;
   RepeatableAnnotation [] raArray = rac.value();
    for (RepeatableAnnotation ra : raArray) {
       System.out.println(ra.value);
   }
}
}
}

執行結果:

I am annotating the 
class

I am annotating the class again
I am annotating the class for the third time.

型別註解

Java 8釋出後,註解可以用於任何型別(Type),這意味著只要可以使用型別的地方就能使用註解。例如,使用新運算子建立類例項、型別轉換、用 implements 實現介面、丟擲異常等,這種註解稱為型別註解。 

這種註解能夠幫助分析與改進 Java 程式,提供更強大的型別檢查。Java 8釋出前,Java 沒有提供型別檢查框架。但是透過型別註解可以開發型別檢查框架,對 Java 程式進行檢查。

舉例來說,假設我們希望特定變數在程式執行過程中始終不為 null。可以編寫一個自定義外掛 NonNull,併為特定變數加上該註解進行檢查。變數宣告如下:

@NonNull String notNullString;

編譯程式碼時,如果發現任何可能將變數賦值為 null 的程式碼,編譯器會檢查潛在問題並給出告警。

自定義註解

Java 允許程式設計師自定義註解。自行定義註解的語法:

public @interface CustomAnnotation { }

上面的程式碼會建立一個 CustomAnnotation新註解。@Interface 關鍵字可用於自定義註解。

自定義註解時,必須設定兩個必填屬性。可以在定義中增加其他屬性,但這兩個重要屬性是必需的,即   Retention Policy 和 Target

這兩個屬性(註解)被用來修飾自定義註解。此外,在自定義註解時可以定義屬性。例如:

@Retention (RetentionPolicy.RUNTIME)

@Target (ElementType.ELEMENT)
public @interface CustomAnnotation {
    public String name () default “Mr Bean”;
    public String dateOfBirth ();
}

上面的自定義註解中,Retention Policy 為 RUNTIME,這表明該註解可以在 JVM 執行時使用;Target 設為 ELEMENT,表示該註解可以修飾任何元素與型別。 

此外,它還具有兩個屬性:name 與 dateOfBirth。其中,name 屬性預設值為 Mr Bean, dateOfBirth 沒有預設值。

注意,宣告的 Method 沒有帶任何引數以及 throws 語句。此外,返回型別僅限於 String、class、enum、註解以及上述型別的陣列。

現在,可以像下面這樣使用自定義註解:

@CustomAnnotation (dateOfBirth = “
1980-06-25”)

public class CustomAnnotatedClass {
}

同樣,可以使用 @Target(ElementType.METHOD) 建立自定義註解修飾 method。

獲取註解及屬性

Java Reflection API 提供了幾種方法,可以在執行時中從 class、method 和其他元素中獲取註解。 

AnnotatedElement介面定義了所有的方法,其中最重要的一個是:

  • getAnnotations(): 返回指定元素的所有註解,包括定義元素時未明確寫出的註解。

  • isAnnotationPresent(annotation): 檢查註解在當前元素上是否可用。

  • getAnnotation(class): 獲取 class 引數使用的註解,如果引數不存在註解返回 null。

這個 class 支援 java.lang.Class、java.lang.reflect.Method 和 java.lang.reflect.Field,基本上可以適用任何的 Java 元素。

下面的示例程式展示瞭如何獲取自定義註解的相關資訊:



public 
static 
void 
main
(String [] args) {

Class object = CustomAnnotatedClass. class;
// 從類中獲取所有註解
Annotation[] annotations = object.getAnnotations();
for( Annotation annotation : annotations ) {
System.out.println(annotation);
}
// 檢查是否存在註解
if( object.isAnnotationPresent( CustomAnnotationClass. class ) ) {
// 獲取需要的註解
Annotation annotation = object.getAnnotation(CustomAnnotationClass. class) ;
System.out.println(annotation);
}
// 獲取註解屬性
for(Annotation annotation : annotations) {
System.out.println(“name: “ + annotation.name());
System.out.println(“Date of Birth: “+ annotation.dateOfBirth());
}
// 對所有方法執行相同的操作
for( Method method : object.getDeclaredMethods() ) {
if( method.isAnnotationPresent( CustomAnnotationMethod. class ) ) {
Annotation annotation = method.getAnnotation(CustomAnnotationMethod. class );
System.out.println( annotation );
}
}
}

總結

註解逐漸成為 J2EE 開發棧的重要組成部分,在開發任何企業級應用都需要用到。 現如今,幾乎所有流行的開發庫出於不同目的都開始使用註解,比如程式碼質量分析、單元測試、XML 解析、依賴項注入等。 大量使用了註解的開發庫有 Hibernate、Spring MVC、Findbugs、JAXB 和 JUnit。  


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2662221/,如需轉載,請註明出處,否則將追究法律責任。

相關文章