註解 是 Java 5 引入的元資料機制,不改變程式語義,但為工具/框架提供豐富語義線索。本文從「如何使用」切入,涵蓋內建註解、元註解配置、自定義註解設計、編譯期處理器原理、運行期反射讀取,並給出實戰最佳實踐。
| 註解 | 用途 | 保留策略 | 典型場景 |
|---|---|---|---|
@Override | 編譯期驗證方法確實覆寫父類/介面 | SOURCE | 防止拼寫錯誤導致意外重載 |
@Deprecated | 標記不建議使用,編譯器警告 | RUNTIME | API 演進期平滑遷移 |
@SuppressWarnings | 抑制特定編譯器警告 | SOURCE | @SuppressWarnings("unchecked") 消除泛型未檢查警告 |
@SafeVarargs (JDK7+) | 聲明可變參數方法不執行不安全操作 | RUNTIME | 消除堆污染警告 |
@FunctionalInterface (JDK8+) | 標記函數式介面 (單一抽象方法) | RUNTIME | 編譯器驗證介面結構 |
這兩個元註解決定自定義註解「能貼在哪裡」與「能活多久」。
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface Resource {
String id() default "";
}
常用目標類型:
TYPE:類、介面、註解、列舉FIELD:欄位、列舉常量METHOD:方法PARAMETER:方法參數CONSTRUCTOR:建構子LOCAL_VARIABLE:區域變數ANNOTATION_TYPE:註解定義 (元註解)PACKAGE:套件聲明TYPE_PARAMETER、TYPE_USE (JDK8+):泛型類型參數、類型使用位置
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME) // 關鍵:需反射讀取必須設 RUNTIME
public @interface Resource {
String id() default "";
}
SOURCE:編譯期有效,.class 不保留 (如 @Override)。CLASS:.class 保留,VM 載入時丟棄 (預設)。RUNTIME:VM 載入並保留,**反射可讀** (框架掃描必備)。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented // 納入 Javadoc
public @interface TestCase {
String id(); // 必填元素
String description() default ""; // 選填,有預設值
int priority() default 1;
String[] tags() default {}; // 陣列元素
}
規則:元素類型限於基本型別、String、Class、列舉、註解類型及其陣列;不可有參數、拋出異常、泛型 (除 Class)。
public class UserService {
@TestCase(id = "TC-001", description = "正常登入", priority = 1, tags = {"smoke", "auth"})
public void login_validCredentials() { ... }
@TestCase(id = "TC-002", description = "密碼錯誤")
public void login_invalidPassword() { ... }
}
public class TestRunner {
public static void runTests(Class<?> clazz) {
for (Method m : clazz.getDeclaredMethods()) {
if (m.isAnnotationPresent(TestCase.class)) {
TestCase tc = m.getAnnotation(TestCase.class);
System.out.printf("執行 %s [%s] 優先級:%d 標籤:%s%n",
m.getName(), tc.id(), tc.priority(), Arrays.toString(tc.tags()));
try {
m.invoke(null); // 假設靜態方法
System.out.println(" → 通過");
} catch (Exception e) {
System.err.println(" → 失敗: " + e.getCause());
}
}
}
}
}
@interface Serializable {}。使用時可省略括號:@Serializable。value,使用時可省略 value =:@Copyright("2024") 等同 @Copyright(value="2024")。@Transactional, JPA @Entity)。應用層開發者通常無需自定義註解,善用框架提供的註解即可。
@Inherited 僅對 @Target(TYPE) 的註解在類繼承鏈生效,**不適用於方法/欄位覆寫**。註解是「程式碼上的便利貼」,配合編譯期處理器或運行期反射,能將聲明式意圖轉化為具體行為。掌握 @Target/@Retention 兩大元註解配置、元素類型限制、反射讀取 API,即可靈活設計自定義註解,或深度理解主流框架運作原理。