整理Effective Java書中Item 39: Prefer annotations to naming patterns心得筆記

主旨

在 Java 裡,如果我們要標記一段程式碼是「特別用途」(像是測試方法),有兩種做法:一種是靠命名,例如把方法命名為 testXxx,另一種則是用註解(Annotation)。這篇建議我們盡量使用註解,而不要依賴命名慣例,因為註解更安全、更具可讀性,也更容易維護。

點出問題:命名慣例的風險

早期 JUnit 的測試方法會透過反射抓所有名稱以 test 開頭的方法執行,像這樣:

public class MyTests {
    public void testAddition() { ... }
    public void testSubtraction() { ... }
}

雖然方便,但這樣做有不少缺點:

  • 如果拼錯名字,測試不會執行,也不會有錯誤訊息提醒你
  • 不容易讓工具(例如 IDE 或建置工具)辨識這些方法的用途
  • 沒有彈性,不能添加額外參數(例如描述、例外處理預期等)

範例:用 @Test 註解改善測試設計

Java 5 之後引進了註解(annotations),JUnit 也跟著改用 @Test 來標示測試方法:

import org.junit.Test;
import static org.junit.Assert.*;

public class MyTests {

    @Test
    public void additionShouldBeCorrect() {
        assertEquals(4, 2 + 2);
    }

    @Test(expected = ArithmeticException.class)
    public void divideByZeroShouldFail() {
        int x = 1 / 0;
    }
}

這樣的好處:

  • 不用靠命名判斷方法用途,更明確
  • 可以加上其他屬性(像是預期例外),可讀性大增
  • IDE 可以用註解自動偵測哪些是測試方法
  • 建置工具與分析工具能更好地支援和整合

劃重點:自己也能寫自定註解

除了內建的註解,我們也可以根據需求自訂註解。例如你要實作一個框架,需要開發者標記某些方法是「可執行任務」:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RunMe {
}

開發者使用:

public class DemoJob {
    @RunMe
    public void sendReport() { ... }

    @RunMe
    public void cleanTempFiles() { ... }
}

然後你可以用反射來找出被標註的方法並執行:

for (Method method : obj.getClass().getDeclaredMethods()) {
    if (method.isAnnotationPresent(RunMe.class)) {
        method.invoke(obj);
    }
}

這樣不但比依靠方法命名更安全,也更彈性。

(可選)真實世界範例:Spring 的註解式開發

Spring Framework 大量使用註解來標記元件或配置,例如:

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable String id) {
        ...
    }
}

這樣的寫法比傳統 XML 設定更清楚易讀,也更容易抽換與測試。

小結

靠命名慣例來標記程式碼用途的方式,不但容易出錯,也難以維護。使用註解不僅語意清晰、支援靜態分析與工具整合,還能提升程式架構的彈性。未來當你需要讓程式有「額外意圖」或「元資料」時,請優先考慮設計成註解,會讓你的程式更強壯、更可維護。