定義於 java.lang.annotation,用於約束自訂註解的使用目標、生命週期、文件化、繼承等語義。
| 元註解 | 作用 | 關鍵屬性值 |
|---|---|---|
@Target | 限定註解可標註的程式元素 | ElementType:TYPE(類/介面/enum/註解)FIELD(欄位/列舉常數)METHOD(方法)PARAMETER(參數)CONSTRUCTOR(建構子)LOCAL_VARIABLE(區域變數)PACKAGE(套件聲明) |
@Retention | 定義註解保留到哪個階段 | RetentionPolicy:SOURCE:源碼,編譯器捨棄CLASS:.class 檔,JVM 載入但不可反射(預設)RUNTIME:執行期可透過反射讀取 (框架常用) |
@Documented | 標示此註解應被 javadoc 納入 API 文件 | 標記註解,無屬性 |
@Inherited | 允許子類別繼承父類別的此註解(僅限 TYPE) | 標記註解;配合 RUNTIME 反射時會向上查找父類別 |
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default ""; // 單屬性慣命名 value()
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
enum Color { RED, GREEN, BLUE }
Color fruitColor() default Color.GREEN;
}
public 或預設(package-private)。default 指定預設值(編譯期常數)。value(),使用時可省略屬性名:@FruitName("Apple")。
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor = FruitColor.Color.RED)
private String appleColor;
// getter/setter 略
}
import java.lang.reflect.Field;
public class FruitInfoScanner {
public static void scan(Object obj) {
Class> clazz = obj.getClass();
System.out.println("掃描類別:" + clazz.getSimpleName());
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
// 讀取 @FruitName
FruitName nameAnno = field.getAnnotation(FruitName.class);
if (nameAnno != null) {
System.out.println(" 欄位:" + field.getName() + " → 水果名稱:" + nameAnno.value());
}
// 讀取 @FruitColor
FruitColor colorAnno = field.getAnnotation(FruitColor.class);
if (colorAnno != null) {
System.out.println(" 欄位:" + field.getName() + " → 顏色:" + colorAnno.fruitColor());
}
}
}
public static void main(String[] args) {
scan(new Apple());
}
}
掃描類別:Apple 欄位:appleName → 水果名稱:Apple 欄位:appleColor → 顏色:RED
若需在編譯期掃描註解並生成新原始碼/資源(如 Lombok、MapStruct、Dagger),需實作 javax.annotation.processing.Processor:
META-INF/services/javax.annotation.processing.Processor 檔案,內容為處理器全類別名。AbstractProcessor,覆寫 process(Set<? extends TypeElement>, RoundEnvironment)。ProcessingEnvironment 提供的 Filer 生成檔案,Messager 輸出編譯訊息。| 項目 | 建議 |
|---|---|
| @Target | 務必精確限定,避免濫用於不相關元素 |
| @Retention | 反射讀取用 RUNTIME;原始碼工具用 SOURCE;預設 CLASS 多無實際用途 |
| 屬性命名 | 單屬性用 value();多屬性用語意化名稱(fruitColor、tableName) |
| 預設值 | 盡量提供合理預設,減少使用者樣板碼 |
| 文件化 | 對外 API 註解加 @Documented,納入 javadoc |
| 繼承 | 類別層級註解需子類可見時加 @Inherited |
自訂註解 = 語義標記 + 元註解約束 + 處理器邏輯。
RUNTIME + 反射 適合輕量級框架整合、執行期配置驗證。
SOURCE + APT 適合生成代碼、編譯期約束檢查、零執行期開銷。
善用兩者分工,打造類型安全、可驗證、自文件化的開發體驗。