開發者常透過繼承、多型、設計模式減少冗餘代碼,但軟體需求的不確定性使得維護與重寫難以避免。本文探討如何善用 Java Reflection API 降低機械性編碼,並進一步引入主動代碼生成克服反射的效能與除錯瓶頸,構建可維護、高效能的資料配置框架。
將外部資料(檔案、XML、資料庫)載入 Java 物件是常見需求。若目標類別結構每週變更,傳統硬編碼載入器需頻繁修改;大型系統中更可能導致維護崩潰。初期嘗試使用 StringTokenizer 手工解析,邏輯雖系統化但難以應對結構變化。
核心思想:以類別定義作為映射元數據,運行時透過反射動態構建物件。
| 欄位類型 | 生成代碼邏輯 |
|---|---|
| String | fileIterator.nextString() |
| Object[] | 迴圈讀取陣列元素,收集至 Vector 再轉陣列 |
| Object | 遞迴呼叫 initializeObject(fileIterator) |
FooFileIterator iter = new FooFileIterator(fileLocation, log);
LoadManager manager = new FooFileLoadManager(iter);
SubFooObject obj = (SubFooObject) ReflectiveObjectLoader.initializeInstance(
SubFooObject.class, manager, log);
Andrew Hunt 與 David Thomas 將代碼生成器分為:
Generated: Total time: 14,481 ms Max Memory: 1,337,672 bytes
Reflection: Total time: 89,219 ms Max Memory: 1,407,944 bytes
採用 MethodCode 基類 + 類型專屬子類(如 MethodForInt)的工廠模式:
public class MethodForInt extends MethodCode {
private final static MethodParameter param =
new MethodParameter(SimpleFileIterator.class, "parser");
public MethodForInt(Class type, CodeBuilder builder) {
super(type, builder);
}
protected String getImplBody(CodeBuilder builder) {
return "return " + param.getName() + ".nextInt();";
}
}
生成輸出:
/** Generated Load method for int **/
final public static int loadint(SimpleFileIterator parser) {
return parser.nextInt();
}
使用 Ant 任務於編譯階段自動生成代碼:
<taskdef name="codegen" classname="com.example.CodeGenTask">
<classpath refid="build.classpath"/>
</taskdef>
<codegen
targetPackage="com.example.generated"
sourceClass="com.example.PurchaseOrder"
fork="yes" />
生成的載入類自動參與後續編譯與單元測試,確保與目標類別同步演進。
反射適合:框架開發、動態代理、註解處理、外掛系統等「結構不確定、需極高動態性」場景。
主動代碼生成適合:資料映射、序列化、ORM、API Client 生成等「結構可預期、追求效能與可維護性」場景。
記住原則:不要在運行時做編譯期能做的事。將反射限制在代碼生成階段,享受動態性優勢的同時,獲得編譯期安全與原生效能。