Java 反射機制應用實踐
引言
Java反射機制是一個非常強大的功能,在很多大型專案比如Spring, Mybatis都可以看見反射的身影。通過反射機制我們可以在執行期間獲取物件的型別資訊,利用這一特性我們可以實現工廠模式和代理模式等設計模式,同時也可以解決Java泛型擦除等令人苦惱的問題。本文我們就從實際應用的角度出發,來應用一下Java的反射機制。
反射基礎
p.s: 本文需要讀者對反射機制的API有一定程度的瞭解,如果之前沒有接觸過的話,建議先看一下官方文件的Quick Start。
在應用反射機制之前,首先我們先來看一下如何獲取一個物件對應的反射類Class
,在Java中我們有三種方法可以獲取一個物件的反射類。
通過getClass方法
在Java中,每一個Object
都有一個getClass
方法,通過getClass方法我們可以獲取到這個物件對應的反射類:
String s = "ziwenxie"; Class<?> c = s.getClass();
通過forName方法
我們也可以呼叫Class
類的靜態方法forName
:
Class<?> c = Class.forName("java.lang.String");
使用.class
或者我們也可以直接使用.class
:
Class<?> c = String.class;
獲取型別資訊
在文章開頭我們就提到反射的一大好處就是可以允許我們在執行期間獲取物件的型別資訊,下面我們通過一個例子來具體看一下。
首先我們在typeinfo.interfacea
包下面新建一個介面A
:
package typeinfo.interfacea; public interface A { void f(); }
接著我們在typeinfo.packageaccess
包下面新建一個介面C
,介面C
繼承自介面A
,並且我們還另外建立了幾個用於測試的方法,注意下面幾個方法的許可權都是不同的。
package typeinfo.packageaccess; import typeinfo.interfacea.A; class C implements A { public void f() { System.out.println("public C.f()"); } public void g() { System.out.println("public C.g()"); } protected void v () { System.out.println("protected C.v()"); } void u() { System.out.println("package C.u()"); } private void w() { System.out.println("private C.w()"); } } public class HiddenC { public static A makeA() { return new C(); } }
在callHiddenMethod()
方法中我們用到了幾個新的API,其中getDeclaredMethod()
根據方法名用於獲取Class類指代物件的某個方法,然後我們通過呼叫invoke()
方法傳入實際的物件就可以觸發物件的相關方法:
package typeinfo; import typeinfo.interfacea.A; import typeinfo.packageaccess.HiddenC; import java.lang.reflect.Method; public class HiddenImplementation { public static void main(String[] args) throws Exception { A a = HiddenC.makeA(); a.f(); System.out.println(a.getClass().getName()); // Oops! Reflection still allows us to call g(): callHiddenMethod(a, "g"); // And even methods that are less accessible! callHiddenMethod(a, "u"); callHiddenMethod(a, "v"); callHiddenMethod(a, "w"); } static void callHiddenMethod(Object a, String methodName) throws Exception { Method g = a.getClass().getDeclaredMethod(methodName); g.setAccessible(true); g.invoke(a); } }
從輸出結果我們可以看出來,不管是public
,default
,protect
還是pricate
方法,通過反射類我們都可以自由呼叫。當然這裡我們只是為了顯示反射的強大威力,在實際開發中這種技巧還是不提倡。
public C.f() typeinfo.packageaccess.C public C.g() package C.u() protected C.v() private C.w()
應用實踐
我們有下面這樣一個業務場景,我們有一個泛型集合類List<Class<? extends Pet>>
,我們需要統計出這個集合類中每種具體的Pet
有多少個。由於Java的泛型擦除,注意類似List<? extends Pet>
的做法肯定是不行的,因為編譯器做了靜態型別檢查之後,到了執行期間JVM會將集合中的物件都視為Pet
,但是並不會知道Pet
代表的究竟是Cat
還是Dog
,所以到了執行期間物件的型別資訊其實全部丟失了。p.s: 關於泛型擦除:我在上一篇文章裡面有詳細解釋,感興趣的朋友可以看一看。
為了實現我們上面的例子,我們先來定義幾個類:
public class Pet extends Individual { public Pet(String name) { super(name); } public Pet() { super(); } } public class Cat extends Pet { public Cat(String name) { super(name); } public Cat() { super(); } } public class Dog extends Pet { public Dog(String name) { super(name); } public Dog() { super(); } } public class EgyptianMau extends Cat { public EgyptianMau(String name) { super(name); } public EgyptianMau() { super(); } } public class Mutt extends Dog { public Mutt(String name) { super(name); } public Mutt() { super(); } }
上面的Pet
類繼承自Individual
,Individual
類的的實現稍微複雜一點,我們實現了Comparable
介面,重新自定義了類的比較規則,如果不是很明白的話,也沒有關係,我們已經將它抽象出來了,所以不理解實現原理也沒有關係。
public class Individual implements Comparable<Individual> { private static long counter = 0; private final long id = counter++; private String name; // name is optional public Individual(String name) { this.name = name; } public Individual() {} public String toString() { return getClass().getSimpleName() + (name == null ? "" : " " + name); } public long id() { return id; } public boolean equals(Object o) { return o instanceof Individual && id == ((Individual)o).id; } public int hashCode() { int result = 17; if (name != null) { result = 37 * result + name.hashCode(); } result = 37 * result + (int) id; return result; } public int compareTo(Individual arg) { // Compare by class name first: String first = getClass().getSimpleName(); String argFirst = arg.getClass().getSimpleName(); int firstCompare = first.compareTo(argFirst); if (firstCompare != 0) { return firstCompare; } if (name != null && arg.name != null) { int secendCompare = name.compareTo(arg.name); if (secendCompare != 0) { return secendCompare; } } return (arg.id < id ? -1 : (arg.id == id ? 0 : 1)); } }
下面建立了一個抽象類PetCreator
,以後我們通過呼叫arrayList()
方法便可以直接獲取相關Pet
類的集合。這裡使用到了我們上面沒有提及的newInstance()
方法,它會返回Class類所真正指代的類的例項,這是什麼意思呢?比如說宣告new Dog().getClass().newInstance()
和直接new Dog()
是等價的。
public abstract class PetCreator { private Random rand = new Random(47); // The List of the different getTypes of Pet to create: public abstract List<Class<? extends Pet>> getTypes(); public Pet randomPet() { // Create one random Pet int n = rand.nextInt(getTypes().size()); try { return getTypes().get(n).newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } public Pet[] createArray(int size) { Pet[] result = new Pet[size]; for (int i = 0; i < size; i++) { result[i] = randomPet(); } return result; } public ArrayList<Pet> arrayList(int size) { ArrayList<Pet> result = new ArrayList<Pet>(); Collections.addAll(result, createArray(size)); return result; } }
接下來我們來實現上面這一個抽象類,解釋一下下面的程式碼,在下面的程式碼中,我們宣告瞭兩個集合類,allTypes
和types
,其中allTypes
中包含了我們呢上面所宣告的所有類,但是我們具體的型別實際上只有兩種即Mutt
和EgypianMau
,所以我們真正需要new
出來的寵物只是types
中所包含的型別,以後我們通過呼叫getTypes()
便可以得到types
中所包含的所喲型別。
public class LiteralPetCreator extends PetCreator { @SuppressWarnings("unchecked") public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList( Arrays.asList(Pet.class, Dog.class, Cat.class, Mutt.class, EgyptianMau.class)); private static final List<Class<? extends Pet>> types = allTypes.subList( allTypes.indexOf(Mutt.class), allTypes.size()); public List<Class<? extends Pet>> getTypes() { return types; } }
總體的邏輯已經完成了,最後我們實現用來統計集合中相關Pet
類個數的TypeCounter
類。解釋一下isAssignalbeFrom()
方法,它可以判斷一個反射類是某個反射類的子類或者間接子類。而getSuperclass()
顧名思義就是得到某個反射類的父類了。
public class TypeCounter extends HashMap<Class<?>, Integer> { private Class<?> baseType; public TypeCounter(Class<?> baseType) { this.baseType = baseType; } public void count(Object obj) { Class<?> type = obj.getClass(); if (!baseType.isAssignableFrom(type)) { throw new RuntimeException( obj + " incorrect type " + type + ", should be type or subtype of " + baseType); } countClass(type); } private void countClass(Class<?> type) { Integer quantity = get(type); put(type, quantity == null ? 1 : quantity + 1); Class<?> superClass = type.getSuperclass(); if (superClass != null && baseType.isAssignableFrom(superClass)) { countClass(superClass); } } @Override public String toString() { StringBuilder result = new StringBuilder("{"); for (Map.Entry<Class<?>, Integer> pair : entrySet()) { result.append(pair.getKey().getSimpleName()); result.append("="); result.append(pair.getValue()); result.append(", "); } result.delete(result.length() - 2, result.length()); result.append("} "); return result.toString(); } }
相關文章
- Java反射機制應用實踐Java反射
- JAVA的反射機制==>用反射分析類的實現Java反射
- 反射機制及應用反射
- Java學習之反射機制及應用場景Java反射
- 探討代理模式與Java反射機制的應用模式Java反射
- Java反射機制Java反射
- Java反射機制實現與原理Java反射
- Java核心反射機制Java反射
- java利器——反射機制Java反射
- Java的反射機制Java反射
- Java反射機制(轉)Java反射
- Java反射機制研究Java反射
- 用Java反射機制模擬hibernateJDBC操作Java反射JDBC
- Java反射機制那些事Java反射
- Java 中的 反射機制Java反射
- Java反射機制簡答Java反射
- 說說 Java 反射機制Java反射
- JAVA(五)反射機制/AnnotationJava反射
- Java 反射機制分析指南Java反射
- Java 反射機制詳解Java反射
- Java 反射最佳實踐Java反射
- Java中的類反射機制Java反射
- Java筆記-反射機制(一)Java筆記反射
- java進階(41)--反射機制Java反射
- 淺析java的反射機制Java反射
- Java註解與反射機制Java反射
- Java反射的應用Java反射
- java反射機制的學習心得Java反射
- 關於Java中的反射機制Java反射
- Java基礎之反射機制(續)Java反射
- Java重點基礎:反射機制Java反射
- Java動態代理和反射機制Java反射
- Java 反射機制的三種方式Java反射
- java中的反射機制淺析Java反射
- Java 反射機制學習資料Java反射
- JAVA中的反射機制詳解Java反射
- 菜鳥學Java(十四)——Java反射機制(一)Java反射
- 菜鳥學Java(十五)——Java反射機制(二)Java反射