Reflect 應用

從 Class 獲取資訊

Class 物件表示所載入的類別,取得Class物件之後,你就可以取得與類別相關聯的資訊,像是套件(package)(別忘了package也是類別名稱的一部 份)、建構方法、方法成員、資料成員等的訊息,而每一個訊息,也會有相應的類別型態,例如套件的對應型態是java.lang.Package,建構方法的對應型態是java.lang.reflect.Constructor,方法成員的對應型態是java.lang.reflect.Method,資料成員的對應型態是java.lang.reflect.Field等。 來看個簡單的示範,以下可以讓您取得所指定類別上的套件名稱:

try {
	Class c = Class.forName(args[0]);
	Package p = c.getPackage();
	System.out.println(p.getName());
} catch (ArrayIndexOutOfBoundsException e) {
	System.out.println("沒有指定類別");
} catch (ClassNotFoundException e) {
	System.out.println("找不到指定類別");
}

你可以分別取回Field、Constructor、Method等物件,分別代表資料成員、建構方法與方法成員,以下範例簡單地實作了取得類別基本資訊的程式:

try {
	Class c = Class.forName(args[0]);
	// 取得套件代表物件
	Package p = c.getPackage();

	System.out.printf("package %s;%n", p.getName());

	// 取得型態修飾,像是class、interface
	int m = c.getModifiers();

	System.out.print(Modifier.toString(m) + " ");
	// 如果是介面
	if (Modifier.isInterface(m)) {
		System.out.print("interface ");
	} else {
		System.out.print("class ");
	}

	System.out.println(c.getName() + " {");

	// 取得宣告的資料成員代表物件
	Field[] fields = c.getDeclaredFields();
	for (Field field : fields) {
		// 顯示權限修飾,像是public、protected、private
		System.out.print("\t" + Modifier.toString(field.getModifiers()));
		// 顯示型態名稱
		System.out.print(" " + field.getType().getName() + " ");
		// 顯示資料成員名稱
		System.out.println(field.getName() + ";");
	}

	// 取得宣告的建構方法代表物件
	Constructor[] constructors = c.getDeclaredConstructors();
	for (Constructor constructor : constructors) {
		// 顯示權限修飾,像是public、protected、private
		System.out.print("\t" + Modifier.toString(constructor.getModifiers()));
		// 顯示建構方法名稱
		System.out.println(" " + constructor.getName() + "();");
	}
	// 取得宣告的方法成員代表物件
	Method[] methods = c.getDeclaredMethods();
	for (Method method : methods) {
		// 顯示權限修飾,像是public、protected、private
		System.out.print("\t" + Modifier.toString(method.getModifiers()));
		// 顯示返回值型態名稱
		System.out.print(" " + method.getReturnType().getName() + " ");
		// 顯示方法名稱
		System.out.println(method.getName() + "();");
	}
	System.out.println("}");
} catch (ArrayIndexOutOfBoundsException e) {
	System.out.println("沒有指定類別");
} catch (ClassNotFoundException e) {
	System.out.println("找不到指定類別");
}

亦可呼叫物件內方法

看看以下範例以最簡單的Get/Set方法

進階應用 - auto get/set value

實戰案例

需求內容:

  1. 輸出前後資料比對結果

  2. 轉換變數名稱,改為可閱讀內容 Ex. status -> 狀態

  3. 可轉換變數資料結果 Ex. 1:啟用, 0: 停用 -> status:1 -> 狀態:啟用

  4. 排序固定,方便查看比對內容

廢話不多,先看結果。

從結果範例可以看到,這種結果通常會應用到後台使用者操作日誌,以及需要紀錄前後結果對照的功能上,以便釐清前後關係。

以下是針對這五個方法的簡單說明:

  1. getAllFields(Class<?> clazz): 這個方法用於獲取一個類及其超類中的所有字段,並根據特定的條件(通過 FieldInfo 注解)進行篩選。如果類不是 CGLIB 代理類,則優先從 fieldsMap 中獲取,否則調用 _getAllFields 方法獲取並存入緩存中。

  2. _getAllFields(Class<?> clazz): 這個私有方法實際上執行字段的提取工作。它首先獲取指定類的所有宣告字段,然後通過 excludeField 方法進行篩選,接著繼續處理超類的字段,直到所有字段被提取並返回。

  3. excludeField(List<Field> fieldList): 這個方法是 _getAllFields 中使用的輔助方法,它通過過濾字段列表,只保留標有 FieldInfo 注解且允許包含(通過 include() 方法)的字段。

  4. extract(Object obj): 這個方法從給定的對象中提取字段信息並創建一個映射,其中包含字段名稱和值。它使用 BeanUtils.getAllFields 獲取所有字段,然後使用反射獲取字段的值。如果該字段允許空值,或者字段值不為空,則將字段信息添加到映射中。

  5. compare(Object from, Object to, String... ignoreFields): 這個方法用於比較兩個對象的字段變更。它使用 extract 方法提取兩個對象的字段信息,然後對比這些信息,找到不同的字段。忽略指定的字段(如果有的話),最終返回一個映射,其中包含變更的字段名稱和相關信息。

  6. transExternalName(ConvertExternalNameWrapper convertWrapper): 這個方法接受一個 ConvertExternalNameWrapper 對象,該對象包含比較結果的映射,將這些比較結果轉換為外部名稱的形式,並返回一個更新後的 ConvertExternalNameWrapper 對象。在這個方法中,首先創建了兩個空的映射 fromMaptoMap 用於存儲轉換後的數據。

這五個方法一起提供了一個工具集,用於處理對象的字段操作、篩選和比較。它們基於反射和 Java 的反射 API,讓你可以更便捷地操作和分析對象的字段數據。

Last updated