Java 5 引入泛型提供編譯期型別安全,並透過 Signature 屬性將型別參數資訊保留於位元碼,擴充反射 API 支援執行期存取。這使反射能深入分析集合元素型別,不再是「死胡同」,對資料綁定、序列化、ORM 等框架極具價值。
Field.getGenericType() 回傳 java.lang.reflect.Type 介面,實際實作為四個子介面:
| 介面 | 用途 | 關鍵方法 |
|---|---|---|
ParameterizedType | 具體型別參數的泛型 | getActualTypeArguments(), getRawType(), getOwnerType() |
GenericArrayType | 元件型別為參數化的陣列 | getGenericComponentType() |
TypeVariable | 型別變數(如 <T>) | getBounds(), getGenericDeclaration(), getName() |
WildcardType | 通配型別 ? extends/super T | getUpperBounds(), getLowerBounds() |
// 泛型配對集合
public class PairCollection<T, U> implements Iterable<T> {
private final ArrayList<T> tValues = new ArrayList<>();
private final ArrayList<U> uValues = new ArrayList<>();
// add, clear, iterator()...
public class PairIterator implements Iterator<T> {
public U matching() { ... } // 取得配對的第二值
}
}
// 目錄資訊
public class DirInfo {
private final List<FileInfo> files;
private final List<DirInfo> directories;
private final Date lastModify;
// getter...
}
// 檔案資訊
public class FileInfo {
private final String name;
private final Date lastModify;
// getter...
}
// 根節點
public class PathDirectory implements Iterable<PairCollection.PairIterator> {
private final PairCollection<String, DirInfo> pathPairs;
// 建構子解析路徑建構資料結構...
}
第一層:解析 PathDirectory.pathPairs 欄位
Field field = PathDirectory.class.getDeclaredField("pathPairs");
ParameterizedType ptype = (ParameterizedType) field.getGenericType();
Class rawType = (Class) ptype.getRawType(); // PairCollection
Type[] actualArgs = ptype.getActualTypeArguments(); // [String, DirInfo]
深入泛型類別定義:透過 Class.getTypeParameters() 取得型別變數
TypeVariable[] tvars = rawType.getTypeParameters();
// tvars[0]: T, upper bound = Object
// tvars[1]: U, upper bound = Object
public class GenericReflector {
private static final Set<String> processed = new HashSet<>();
public static void analyze(Class<?> clazz) {
if (shouldSkip(clazz)) return;
processed.add(clazz.getName());
System.out.println("Class " + clazz.getName() + " details:");
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
describeField("", field);
}
}
private static void describeField(String indent, Field field) {
Class baseType = field.getType();
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
System.out.println(indent + field.getName() + " is parameterized: " + pt.getRawType());
Type[] args = pt.getActualTypeArguments();
for (Type arg : args) {
if (arg instanceof Class) analyze((Class<?>) arg);
}
} else if (genericType instanceof GenericArrayType) {
Type comp = ((GenericArrayType) genericType).getGenericComponentType();
// 處理陣列元件型別...
}
// 遞迴分析基礎型別
analyze(baseType);
}
}
Class PathDirectory details:
pathPairs is parameterized: PairCollection
using types (String, DirInfo)
Class DirInfo details:
files is parameterized: List using types (FileInfo)
directories is parameterized: List using types (DirInfo)
Class FileInfo details:
name: String
lastModify: Date
Class PairCollection details:
tValues is parameterized: ArrayList using types (T)
uValues is parameterized: ArrayList using types (U)
Class ArrayList details:
elementData: array type E[]
size: int
反射回傳的實際型別引數可能為 TypeVariable(如 T, U, E),需在遞迴過程中維護型別替換對應表:
PairCollection 定義 <T, U>PairCollection<String, DirInfo> → 對應:T→String, U→DirInfoArrayList<T> 中 E 實為 T → 遞迴時帶入對應表解析 E→String完整實作需在每層遞迴建立並傳遞 Map<TypeVariable, Type> 替換上下文。
Java 5 泛型反射 API 雖複雜,但提供前所未有的型別內省能力。掌握 ParameterizedType、TypeVariable 交互解析與替換追蹤機制,是建構新一代型別感知工具(資料綁定、序列化、API 文件生成)的關鍵基礎。