Java 註解 (Annotations)
註解是一種後設資料(metadata),可以為程式碼提供額外資訊,用於編譯檢查、程式碼生成、執行時處理等。
內建註解
@Override
public class Child extends Parent {
@Override // 確保正確覆寫父類別方法
public void doSomething() {
// ...
}
}
@Deprecated
@Deprecated
public void oldMethod() {
// 已棄用的方法
}
@Deprecated(since = "1.5", forRemoval = true)
public void veryOldMethod() {
// 將在未來版本移除
}
@SuppressWarnings
@SuppressWarnings("unchecked")
public void method() {
List list = new ArrayList(); // 不會產生警告
}
@SuppressWarnings({"unchecked", "deprecation"})
public void anotherMethod() {
// 抑制多種警告
}
@FunctionalInterface
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
// 只能有一個抽象方法
}
@SafeVarargs
@SafeVarargs
public final <T> void process(T... items) {
for (T item : items) {
System.out.println(item);
}
}
自訂註解
基本語法
public @interface MyAnnotation {
String value();
int count() default 1;
}
// 使用
@MyAnnotation(value = "test", count = 5)
public class MyClass {}
// value 可以省略屬性名
@MyAnnotation("test")
public class MyClass2 {}
元註解
import java.lang.annotation.*;
@Target(ElementType.METHOD) // 可用於哪些元素
@Retention(RetentionPolicy.RUNTIME) // 保留到何時
@Documented // 包含在 Javadoc
@Inherited // 子類別繼承
public @interface MyAnnotation {
String value();
}
@Target 選項
| 值 | 說明 |
|---|
| TYPE | 類別、介面、列舉、Record |
| FIELD | 欄位 |
| METHOD | 方法 |
| PARAMETER | 方法參數 |
| CONSTRUCTOR | 建構子 |
| LOCAL_VARIABLE | 區域變數 |
| ANNOTATION_TYPE | 其他註解 |
| PACKAGE | 套件 |
| TYPE_PARAMETER | 型別參數 |
| TYPE_USE | 型別使用 |
@Retention 選項
| 值 | 說明 |
|---|
| SOURCE | 只在原始碼,編譯後丟棄 |
| CLASS | 保留到 .class 檔,執行時不可用(預設) |
| RUNTIME | 執行時可透過反射取得 |
執行時讀取註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
String name() default "";
}
public class MyTest {
@Test(name = "測試方法1")
public void test1() {}
@Test
public void test2() {}
}
// 讀取註解
Class<?> clazz = MyTest.class;
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Test.class)) {
Test test = method.getAnnotation(Test.class);
System.out.println(method.getName() + ": " + test.name());
}
}
實用範例
驗證註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotNull {
String message() default "不能為空";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default Integer.MAX_VALUE;
}
public class User {
@NotNull
private String name;
@Range(min = 0, max = 150)
private int age;
}
// 驗證器
public class Validator {
public static void validate(Object obj) throws Exception {
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(NotNull.class)) {
if (field.get(obj) == null) {
NotNull ann = field.getAnnotation(NotNull.class);
throw new IllegalArgumentException(ann.message());
}
}
if (field.isAnnotationPresent(Range.class)) {
Range range = field.getAnnotation(Range.class);
int value = (int) field.get(obj);
if (value < range.min() || value > range.max()) {
throw new IllegalArgumentException("值必須在 " + range.min() + " 到 " + range.max() + " 之間");
}
}
}
}
}
可重複註解
@Repeatable(Schedules.class)
public @interface Schedule {
String day();
String time();
}
public @interface Schedules {
Schedule[] value();
}
@Schedule(day = "Mon", time = "09:00")
@Schedule(day = "Wed", time = "14:00")
public class Meeting {}
// 讀取
Schedule[] schedules = Meeting.class.getAnnotationsByType(Schedule.class);
常見框架註解
// Spring
@Component
@Service
@Repository
@Controller
@Autowired
@Value
// JPA
@Entity
@Table
@Id
@Column
@OneToMany
// Jackson
@JsonProperty
@JsonIgnore
// JUnit
@Test
@BeforeEach
@AfterEach
重點整理
- 註解是後設資料,為程式碼提供額外資訊
@Override、@Deprecated、@SuppressWarnings 是常用內建註解- 使用
@interface 定義自訂註解 @Target 指定可用位置,@Retention 指定保留時機RUNTIME 保留才能在執行時透過反射讀取- 許多框架大量使用註解進行設定