EasyReflect是開源基礎組件集成庫EasyAndroid中的基礎組件之一。java
其做用是:對基礎反射api進行封裝,精簡反射操做android
EasyAndroid做爲一款集成組件庫,此庫中所集成的組件,均包含如下特色,你能夠放心使用~~git
1. 設計獨立github
組件間獨立存在,不相互依賴,且若只須要集成庫中的部分組件。也能夠很方便的
只copy對應的組件文件
進行使用api
2. 設計輕巧數組
由於是組件集成庫,因此要求每一個組件的設計儘可能精練、輕巧。避免由於一個小功能而引入大量無用代碼.bash
每一個組件的方法數均
不超過100
. 大部分組件甚至不超過50
。ui
得益於編碼時的高內聚性
,若你只須要使用EasyReflect. 那麼能夠直接去拷貝EasyReflect源碼文件到你的項目中,直接進行使用,也是沒問題的。編碼
EasyAndroid開源庫地址:spa
https://github.com/yjfnypeu/EasyAndroid
EasyReflect組件地址:
https://github.com/yjfnypeu/EasyAndroid/blob/master/utils/src/main/java/com/haoge/easyandroid/easy/EasyReflect.kt
class EasyReflect private constructor(val clazz: Class<*>, private var instance:Any?)
複製代碼
能夠看到:EasyReflect自己持有兩部分數據:clazz
與instance
.
請注意:對於instance數據來講,當執行操做過程當中須要使用此instance實例時(好比讀取某個成員變量),若此時instance爲null,則將觸發使用默認的空構造器進行instance建立。
val reflect = EasyReflect.create(Test::class.java)
複製代碼
val reflect = EasyReflect.create("com.haoge.sample.Test")
// 也可指定ClassLoader進行加載
val reflect = EasyReflect.create(className, classLoader)
複製代碼
val reflect = EasyReflect.create(any)
複製代碼
瞭解了EasyReflect的幾種建立方式與其區別後。咱們就能夠正式進行使用了
val reflect:EasyReflect = createReflect()
// 經過默認空構造器進行對象建立. 經過get方法獲取建立出的對象
val instance = reflect.instance().get<Any>()
// 經過含參構造器進行對象建立
val instance2 = reflect.instance(arg0, arg1...argN).get<Any>()
複製代碼
EasyReflect對字段的操做,不用再去考慮它是不是靜態的
或者仍是final修飾的
。更不用去管字段是不是private
不可見的。咱們只須要指定字段名便可。這大大加強了便利性!
val value:Any = reflect.getFieldValue<Any>(fieldName)
複製代碼
reflect.setField(fieldName, newValue)
複製代碼
EasyReflect
實例進行使用val newReflect = reflect.transform(fieldName)
複製代碼
與字段操做相似,咱們也不用去擔憂方法的可見性問題。須要的只有此方法存在
便可
// 調用無參方法
reflect.call(methodName)
// 調用有參方法
reflect.call(methodName, arg0, arg1...argN)
複製代碼
val value = reflect.callWithReturn(methodName, arg0, arg1...argN).get()
複製代碼
可變參數會在編譯時。替換爲數組的形式。因此使用反射進行可變參數的傳參時,須要使用數組的形式對參數進行構建:
如下方的兩個方法爲例:
class Method{
fun onlyVararg(vararg names:String)
fun withVararg(preffix:Int, vararg names:String)
}
複製代碼
此類中的兩個方法均爲帶有可變參數的方法,在反射環境下。就應該將其中的可變參數看做爲是對應的數組進行使用,因此這個時候。咱們須要使用以下方式進行參數傳遞:
// 1. 調用只含有可變參數的方法
reflect.call("onlyVararg", arrayOf("Hello", "World"))
// 2. 調用包含其餘參數的可變參數方法
reflect.call("withVararg", 1, arrayOf("Hello", "World"))
複製代碼
在這裏要特別提醒一下:因爲java與kotlin的差別性,在java中調用只含有可變參數的方法時。須要對參數進行特殊處理(外層包裹一層Object[]
):
EasyReflect reflect = EasyReflect.create(Method.class)
reflect.call("onlyVararg", new Object[]{new String[]{"Hello", "World"}})
複製代碼
而對於withVararg
這種帶有非可變參數的方法則不用進行此特殊處理:
reflect.call("withVararg", 1, new String[]{"Hello", "World"})
複製代碼
這是由於在java中。若實參爲一個數組時。這個時候將會對此數組進行解構平鋪,因此咱們須要在外層再單獨套一層Object數組。才能對參數類型作到正確匹配。而在kotlin中不存在此類問題。
當你須要對某個類進行訪問,可是又不想經過寫死名字的方式去調用時,可使用此特性:
經過動態代理的方式,建立一個代理類來對反射操做進行託管
仍是經過舉例來講明:
class Test {
private fun function0(name:String)
}
複製代碼
對於上面所舉例的類來講。要經過反射調用它的function0方法,那麼須要這樣寫:
val reflect = EasyReflect.create(Test::class.java)
reflect.call("function0", "This is name")
複製代碼
而若使用託管的方式。配置先建立代理接口,而後經過代理接口進行方法訪問:
// 建立代理接口
interface ITestProxy {
fun function0(name:String)
}
val reflect = EasyReflect.create(Test::class.java)
// 建立託管代理實例:
val proxy:ITestProxy = reflect.proxy(ITestProxy::class.java)
// 而後直接經過代理類進行操做:
proxy.function0("proxy name")
複製代碼
固然,若是隻能對方法進行託管那這功能就有點太弱逼了。託管方案也同時支持了對變量的操做:
class Test {
// 添加一個字段
private val user:User
private fun function0(name:String)
}
// 建立代理接口
interface ITestProxy {
fun function0(name:String)
//==== 對user字段進行賦值
// 方式一:使用setXXX方法進行賦值
fun setUser(user:User)
// 方式二:使用set(name, value)方法進行賦值
fun set(fieldName:String, value:String)
//==== 對user進行取值
// 方式一:使用getXXX方法進行取值
fun getUser()
// 方式二:使用get(name)方法進行取值
fun get(fieldName:String)
}
val reflect = EasyReflect.create(Test::class.java)
// 建立託管代理實例:
val proxy:ITestProxy = reflect.proxy(ITestProxy::class.java)
// 而後直接經過代理類進行操做:
proxy.setUser(user)// 方式一賦值
proxy.set("user", user)// 方式二賦值
proxy.getUser() // 方式一取值
proxy.get("user")// 方式二取值
複製代碼
從某種程度上來講:代理託管方案會比通常的直接反射操做要繁瑣一點。可是帶來的便利也是顯而易見的:
首當其衝的優勢就是:託管方式寫的代碼比直接進行反射操做更加優雅~
其次,在某些使用環境下,好比在團隊協做中:能夠將對外不方便直接提供實例的經過託管代理實例進行提供。
爲了方便你們更好的理解,這一節中我會列舉一些小案例,方便你們更清楚的知道應該怎麼去使用:
假設咱們有如下一個類須要建立:
data class Example private constructor(val name: String)
複製代碼
那麼咱們能夠這樣來進行建立:
val example = EasyReflect.create(Example::class.java)// 指定實體類的class
.instance("Hello World")// 根據對應的構造器的入參類型進行傳參
.get<Example>() // 獲取建立好的實體類實例
複製代碼
假設咱們須要對此類中name變量進行操做
class Example {
private name:String? = null
}
複製代碼
由於是成員變量的操做。而成員變量是屬於具體實例
的。因此首先應該須要獲取到具體實例進行操做:
val example = Example()
val reflect = EasyReflect.create(example)
複製代碼
獲取到實例後,便可直接操做了:
賦值
reflect.setField("name"/*變量名*/, "Hello"/*賦的值*/)
複製代碼
取值
val name = reflect.getFieldValue<String>("name")
複製代碼
class Example {
companion object {
@JvmStatic
val staticName:String? = null
}
}
複製代碼
由於靜態變量是隸屬於類自己的,因此與成員變量的訪問不一樣:靜態變量的訪問只須要使用類自己便可:
val reflect = EasyReflect.create(Example::class.java)
複製代碼
固然,你也能夠跟成員變量的操做同樣的,直接提供具體的類實例進行使用, 都是同樣的:
val reflect = EasyReflect.create(example)
複製代碼
賦值
reflect.setField("staticName", "Haoge")
複製代碼
取值
val staticName:String = reflect.getFieldValue<String>("staticName")
複製代碼
class Example {
private fun transform(name:String):User {...}
}
複製代碼
與成員變量
相似,成員方法
也是隸屬於具體的類實例的,因此建立時須要提供具體的類實例
val reflect = EasyReflect.create(Example())
複製代碼
而後,EasyReflect
中,對方法提供兩種操做:
1. 直接訪問指定的方法
reflect.call(
"transform"/*第一個參數:方法名*/,
"Haoge" /*可變參數,須要與具體方法的傳參類型一致*/
)
複製代碼
2. 訪問指定方法,並使用返回值建立新的Reflect實例進行返回
val userReflect = reflect.callWithReturn(
"transform"/*第一個參數:方法名*/,
"Haoge" /*可變參數,須要與具體方法的傳參類型一致*/
)
// 獲取到返回的user實例
val user:User = userReflect.get<User>()
// 也能夠根據返回的userReflect繼續對User類進行操做
...
複製代碼
靜態方法
也是隸屬於類自己
的,因此說:與靜態變量的訪問
相似,只須要指定操做的類便可直接訪問了:
val reflect = EasyReflect.create(Example::class.java)
複製代碼
其餘具體的操做方式與成員方法的操做均一致。
class Example {
val user:InternalUser
}
private class InternalUser {
val name:String
}
複製代碼
相似上方代碼:咱們須要獲取變量user
中的name
數據,可是user的類是私有的,外部不能訪問。
因此,按照上面咱們提供的方式。咱們須要這樣來作一次中轉後再使用:
val reflect = EasyReflect.create(example)
// 由於外部不能訪問到InternalUser,因此用頂層類進行接收
val user:Any = reflect.getFieldValue<Any>("user")
// 再使用具體的user實例建立出新的操做類
val userReflect = EasyReflect.create(user)
// 最後再讀取InternalUser中的name字段:
val name:String = userReflect.getFieldValue<String>("name")
複製代碼
能夠看到這種方式仍是比較繁瑣的,因此EasyReflect
專門針對此種操做,提供了transform
方法,最終你可使用下方的代碼作到與上面一樣的效果:
val name:String = EasyReflect.create(example)
.transform("user")// 使用user變量的數據生成新的reflect實例
.get<String>("name")// 讀取InternalUser中的name數據。
複製代碼
所謂的動態代理託管,便是經過動態代理
的方式,將一個代理接口
與實體類
進行綁定,使得能夠經過操做綁定的代理類
作到操做對應實體類
的效果
class Example {
fun function() {}
}
複製代碼
好比這裏咱們須要使用動態代理託管
的方式訪問此function
方法,那麼首先,須要先建立一個代理接口
interface Proxy {
// 與Example.function方法對應,須要同名同參數才能正確映射匹配。
fun function();
}
複製代碼
如今咱們將此代理接口
與對應的被代理實例example
進行綁定:
val proxy:Proxy = EasyReflect.create(example).proxy(Proxy::class.java)
複製代碼
綁定後便可經過自動建立的代理實例proxy
直接進行操做了:
proxy.function() ==等價於==> example.function()
複製代碼
固然,也能夠對被代理實例
中的變量進行代理,好比:
class Example {
val user:User = User()
}
複製代碼
當咱們須要對此類中的user
變量進行取值時。能夠建立如下幾種的代理方法
get
,且參數有且只有一個
,類型爲String的:fun get(name:String):Any?
複製代碼
getter
方法:fun getUser():User? {}
複製代碼
此兩種代理方法
都是等價的,因此經過代理接口進行操做時。他們的效果都是一致的:
proxy.get("user") ==等價於==> example.user
proxy.getUser() ==等價於==> example.user
複製代碼
一樣的,當咱們須要對類中變量進行賦值時,也提供兩種代理方法建立方式
set
,且有錢僅有兩個
參數。第一個參數類型爲String
:fun set(name:String, value:Any)
複製代碼
setter
方法:fun setUser(user:User)
複製代碼
因此,使用此賦值方法的效果爲:
proxy.set("user", newUser) ==等價於==> example.user = newUser
proxy.setUser(newUser) ==等價於==> example.user = newUser
複製代碼
但願這些示例能讓你們在使用時有所幫助~
歡迎掃描下方二維碼關注個人公衆號👇