發(fā)現(xiàn)已經(jīng)有一個(gè)月左右沒(méi)更新文章了,好吧赢底,真的沒(méi)什么好寫的失都,一直在努力工作工作工作柏蘑。突然發(fā)現(xiàn)以前好像沒(méi)寫過(guò)反射的文章,終于可以偷雞一回了粹庞,這次咳焚,純伸手黨福利好嗎,封裝反射工具類庞溜,想用直接拿去用(項(xiàng)目地址在最下方)革半。
一. 反射調(diào)用
我今天找Api的時(shí)候發(fā)現(xiàn)一篇寫得挺好的文章,http://www.reibang.com/p/9be58ee20dee
這位大佬把大部分常用的方法都列舉出來(lái)了流码,我就不重復(fù)寫了又官。
并且這篇不打算寫原理,純寫調(diào)用漫试。
二. 封裝
1.獲取class對(duì)象
在反射中六敬,最主要的就是class對(duì)象,無(wú)論你想反射獲取參數(shù)驾荣,還是調(diào)用方法外构,都需要先獲取到class對(duì)象,獲取class對(duì)象的方法很多播掷,我這里只寫了根據(jù)name來(lái)獲取
public static Class getClass(String clsName){
try {
if (!TextUtils.isEmpty(clsName)){
Class<?> cls = Class.forName(clsName);
return cls;
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
還有一點(diǎn)审编,為什么獲取Class 對(duì)象要單獨(dú)拿出來(lái)做一個(gè)方法,因?yàn)橛幸恍┨厥獾膱?chǎng)景不一定能用類名來(lái)獲取到Class 對(duì)象歧匈,比如一些插件化框架垒酬,有提供自己的獲取Class 對(duì)象的方法,所以需要單獨(dú)封裝成一個(gè)方法件炉。
2.調(diào)用構(gòu)造方法
封裝調(diào)用構(gòu)造方法伤溉,主要用到Constructor
public static Object getConstructor(Class cls, Class[] paramsType, Object[] params){
Object result = null;
try {
if (cls != null){
Constructor constructor = cls.getConstructor(paramsType);
result = constructor.newInstance(params);
}
}catch (Exception e){
e.printStackTrace();
}
return result;
}
可以簡(jiǎn)單看看getConstructor和newInstance源碼
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getConstructor0(parameterTypes, Member.PUBLIC);
}
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (serializationClass == null) {
return newInstance0(initargs);
} else {
return (T) newInstanceFromSerialization(serializationCtor, serializationClass);
}
}
一個(gè)是傳Class不定參數(shù),另一個(gè)是傳Object不定參數(shù)妻率,所以我這邊封裝的時(shí)候定義了兩個(gè)數(shù)組參數(shù)乱顾。
調(diào)用的時(shí)候
Class[] clsA = new Class[]{String.class};
Object[] objA = new Object[]{"參數(shù)1"};
BaseReflectUtils.getConstructor(cls, clsA, objA);
其實(shí)調(diào)用無(wú)參的構(gòu)造方法也可以直接使用newInstance方法
cls.newInstance()
3.獲取參數(shù)
反射獲取參數(shù),也就是調(diào)用靜態(tài)變量宫静,用到Field
可能網(wǎng)上一般會(huì)這樣寫
Field daf = cls.getField(fname);
result = daf.get(null);
但是如果你直接這樣使用的話走净,你會(huì)發(fā)現(xiàn),靜態(tài)參數(shù)能正常調(diào)用孤里,非靜態(tài)參數(shù)你就拿不到值了伏伯,如果稍微了解一點(diǎn)原理的話就知道為什么了。get方法傳的參數(shù)是對(duì)象捌袜,哪個(gè)對(duì)象的參數(shù)说搅,靜態(tài)參數(shù)不用對(duì)象,所以可以直接傳null虏等。那么可以這樣寫
public static Object getField(Class cls, String fname){
return getField(cls, fname, null);
}
public static Object getField(Class cls, String fname, Object obj){
Object result = null;
try {
Field daf = cls.getField(fname);
// 反射靜態(tài)參數(shù)直接傳空就行弄唧,反射非靜態(tài)參數(shù)要使用相對(duì)應(yīng)的對(duì)象
result = daf.get(obj);
}catch (Exception e){
e.printStackTrace();
}
return result;
}
靜態(tài)參數(shù)就調(diào)第一個(gè)方法适肠,非靜態(tài)就調(diào)下面的并傳入對(duì)象。
4.調(diào)用方法
調(diào)用方法用的是Method
一般寫
Method method = cls.getMethod(mname, paramsType);
object = method.invoke(obj, params);
但是要考慮靜態(tài)和非靜態(tài)的情況候引,靜態(tài)和非靜態(tài)其實(shí)就是像上面一樣區(qū)別在于是否需要對(duì)象侯养。還要考慮是否要傳參數(shù),也是和上面一樣澄干,區(qū)別在于是不是會(huì)多出兩個(gè)參數(shù)逛揩,所以我這邊這樣寫。
// 靜態(tài)無(wú)參
public static Object getMethod(Class cls, String mname){
return getMethod(cls, mname, null, null, null);
}
// 靜態(tài)有參
public static Object getMethod(Class cls, String mname, Class[] paramsType, Object[] params){
return getMethod(cls, mname, paramsType, params, null);
}
// 非靜態(tài)無(wú)參
public static Object getMethod(Class cls, String mname, Object obj){
return getMethod(cls, mname, null, null, obj);
}
public static Object getMethod(Class cls, String mname, Class[] paramsType, Object[] params, Object obj){
Object object = null;
try {
Method method = cls.getMethod(mname, paramsType);
object = method.invoke(obj, params);
}catch (Exception e){
e.printStackTrace();
}
return object;
}
我就只舉個(gè)栗子麸俘,非靜態(tài)有參數(shù)的情況就這樣調(diào)用
Class cls = BaseReflectUtils.getClass("com.kylim.fstest.TestD");
Object obj = BaseReflectUtils.getConstructor(cls,new Class[0], new Object[0]);
Class[] clsA = new Class[]{int.class};
Object[] objA = new Object[]{101};
String result = (String) BaseReflectUtils.getMethod(cls, "testTwo", clsA, objA, obj);
會(huì)發(fā)現(xiàn)傳的參數(shù)有那么一點(diǎn)多辩稽,如果你覺(jué)得不好看,可以封裝個(gè)鏈?zhǔn)秸{(diào)用
public static class Builder{
private Class cls;
private String mname;
private Class[] paramsType;
private Object[] params;
private Object obj;
public Builder setCls(Class cls) {
this.cls = cls;
return this;
}
public Builder setMname(String mname) {
this.mname = mname;
return this;
}
public Builder setParamsType(Class[] paramsType) {
this.paramsType = paramsType;
return this;
}
public Builder setParams(Object[] params) {
this.params = params;
return this;
}
public Builder setObj(Object obj) {
this.obj = obj;
return this;
}
public Object build(){
// 類名和方法名不能為空
if (cls == null || TextUtils.isEmpty(mname)){
return null;
}
return getMethod(cls, mname, paramsType, params, obj);
}
}
這樣調(diào)用的時(shí)候
String result = (String) new BaseReflectUtils.Builder().setCls(cls)
.setMname("testTwo")
.setParamsType(new Class[]{int.class})
.setParams(new Object[]{101})
.setObj(BaseReflectUtils.getConstructor(cls,new Class[0], new Object[0]))
.build();
確實(shí)參數(shù)會(huì)有點(diǎn)多从媚,會(huì)讓人有一種封不封裝沒(méi)啥區(qū)別逞泄,首先封裝的話有利于之后的擴(kuò)展,其次静檬,你可以對(duì)常用的反射(固定的名字或參數(shù)類型這些)就行二次封裝。
5. 調(diào)用子類
上面已經(jīng)講了最基礎(chǔ)的調(diào)用參數(shù)和方法兩種并级,下面就相當(dāng)于擴(kuò)展拂檩,如果存在子類的情況呢,要如何進(jìn)行封裝嘲碧。
在反射中獲取子類需要用到getClasses方法
可以這樣寫
public static Class getChildClass(String clsName, String childClsName){
try {
if (!TextUtils.isEmpty(clsName)){
Class<?> cls = Class.forName(clsName);
Class[] children = cls.getClasses();
if (children != null && !TextUtils.isEmpty(childClsName)){
for (int i = 0; i < children.length; i++) {
if (childClsName.equals(children[i].getSimpleName())){
return children[i];
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
在外部可以調(diào)用
Class cls = KlReflectUtils.getChildClass("com.kylim.fstest.TestE", "ChildTestE");
這樣就能獲取到對(duì)應(yīng)的Class 對(duì)象稻励,有了Class 對(duì)象之后,你想怎么反射就可以參照上邊愈涩。
6. 單例
平時(shí)寫代碼時(shí)也會(huì)用到單例望抽,反射寫得不多的話,可能一碰到單例就有點(diǎn)蒙了履婉。
其實(shí)單例也是獲取到對(duì)象然后調(diào)用對(duì)象的方法煤篙。
假設(shè)我寫這樣的一個(gè)單例
public class TestF {
private static volatile TestF testF;
public static TestF getInstance() {
if (testF == null) {
synchronized (TestF.class) {
if (testF == null) {
testF = new TestF();
}
}
}
return testF;
}
public void testOne(){
Log.v("testOne", "調(diào)用testOne");
}
}
平時(shí)直接引用的情況是這樣寫
TestF.getInstance().testOne()
在反射中結(jié)合我們上面的封裝,我們可以這樣寫
Class cls = BaseReflectUtils.getClass("com.kylim.fstest.TestF");
BaseReflectUtils.getMethod(cls, "testOne", BaseReflectUtils.getMethod(cls, "getInstance"));
BaseReflectUtils.getMethod(cls, "getInstance")就是對(duì)象
7.Builder模式
最后說(shuō)一個(gè)特殊的栗子毁腿,Builder模式有時(shí)也會(huì)常用辑奈,比如參數(shù)多的時(shí)候,這樣對(duì)Builder模式的對(duì)象進(jìn)行反射的話就有點(diǎn)蛋疼已烤,我們舉個(gè)栗子鸠窗。
假如我寫個(gè)Builder類
public class TestG {
private TestG(){
}
private TestG(Builder builder){
Log.v("TestG", "結(jié)果:"+builder.code+" "+builder.msg);
}
public static class Builder{
private int code;
private String msg;
public Builder setCode(int code) {
this.code = code;
return this;
}
public Builder setMsg(String msg) {
this.msg = msg;
return this;
}
public TestG build(){
return new TestG(this);
}
}
}
這個(gè)Demo里面假設(shè)有兩個(gè)參數(shù),實(shí)際上肯定不止兩個(gè)胯究,因?yàn)閮蓚€(gè)就沒(méi)必要用Builder稍计。
那么,我們寫反射的時(shí)候需要這樣寫
Class cls = KlReflectUtils.getChildClass("com.kylim.fstest.TestG", "Builder");
// 構(gòu)造方法
Object obj = BaseReflectUtils.getConstructor(cls, new Class[0], new Object[0]);
// 調(diào)用第一個(gè)方法
Class[] clsA = new Class[]{int.class};
Object[] objA = new Object[]{200};
Object obj1 = BaseReflectUtils.getMethod(cls, "setCode", clsA, objA, obj);
// 調(diào)用第二個(gè)方法
Class[] clsB = new Class[]{String.class};
Object[] objB = new Object[]{"請(qǐng)求成功"};
Object obj2 = BaseReflectUtils.getMethod(cls, "setMsg", clsB, objB, obj1);
// 調(diào)用第三個(gè)方法
BaseReflectUtils.getMethod(cls, "build", obj2);
可以看到才2個(gè)參數(shù)就需要寫這么多代碼裕循,參數(shù)多的話到時(shí)候改其中一個(gè)臣嚣,找到都煩了净刮,所以需要封裝一下。
/**
* Builder模式的反射
*/
public static class Builder{
private Class cls;
private Object object;
public Builder(Class cls, Object obj){
this.cls = cls;
this.object = obj;
}
public Builder setBuilderData(String mname, Class[] paramsType, Object[] params){
object = BaseReflectUtils.getMethod(cls, mname, paramsType, params, object);
return this;
}
public Object build(){
return build(null, null);
}
public Object build(Class[] paramsType, Object[] params){
return BaseReflectUtils.getMethod(cls, "build", paramsType, params, object);
}
}
我這里用Builder來(lái)封裝茧球,每個(gè)人都可以根據(jù)自己的喜好來(lái)做
然后我們調(diào)用的時(shí)候庭瑰,就可以寫成這樣
new KlReflectUtils.Builder(cls, obj)
.setBuilderData( "setCode", new Class[]{int.class}, new Object[]{200})
.setBuilderData( "setMsg", new Class[]{String.class}, new Object[]{"請(qǐng)求成功"})
.build();
如果有10個(gè)參數(shù)的話就這樣
new KlReflectUtils.Builder(cls, obj)
.setBuilderData( "setCode", new Class[]{int.class}, new Object[]{200})
.setBuilderData( "setMsg", new Class[]{String.class}, new Object[]{"請(qǐng)求成功"})
.setBuilderData( .......)
.setBuilderData( .......)
.setBuilderData( .......)
.setBuilderData( .......)
.setBuilderData( .......)
.setBuilderData( .......)
.setBuilderData( .......)
.setBuilderData( .......)
.build();
三. 代碼地址
直接上地址,想用的可以直接去拿來(lái)用抢埋,主要是BaseReflectUtils和KlReflectUtils弹灭,BaseReflectUtils是基本的反射的封裝,KlReflectUtils是對(duì)常用的進(jìn)行二次封裝揪垄,其實(shí)代碼并不多穷吮。
https://github.com/994866755/handsomeYe.reflectDemo/tree/master