EasyAndroid基础集成组件库之:EasyReflect 优雅的反射功能库

什么是EasyReflect

EasyReflect是开源基础组件集成库EasyAndroid中的基础组件之一。java

其做用是:对基础反射api进行封装,精简反射操做android

EasyAndroid做为一款集成组件库,此库中所集成的组件,均包含如下特色,你能够放心使用~~git

1. 设计独立github

组件间独立存在,不相互依赖,且若只须要集成库中的部分组件。也能够很方便的只copy对应的组件文件进行使用api

2. 设计轻巧数组

由于是组件集成库,因此要求每一个组件的设计尽可能精练、轻巧。避免由于一个小功能而引入大量无用代码.bash

每一个组件的方法数均不超过100. 大部分组件甚至不超过50ui

得益于编码时的高内聚性,若你只须要使用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

用法

1. 初识EasyReflect

class EasyReflect private constructor(val clazz: Class<*>, private var instance:Any?)
复制代码

能够看到:EasyReflect自己持有两部分数据:clazzinstance.

  • clazz: 此实例所绑定操做的clazz实例。永不为null
  • instance: 此实例所绑定的instance实例,为clazz类型的具体实例。可能为null。

请注意:对于instance数据来讲,当执行操做过程当中须要使用此instance实例时(好比读取某个成员变量),若此时instance为null,则将触发使用默认的空构造器进行instance建立。

2. 建立EasyReflect的几种姿式

  1. 只使用Class进行建立:建立后只含有clazz数据
val reflect = EasyReflect.create(Test::class.java)
复制代码
  1. 使用类全名进行建立(可指定ClassLoader):建立后只含有clazz数据
val reflect = EasyReflect.create("com.haoge.sample.Test")
// 也可指定ClassLoader进行加载
val reflect = EasyReflect.create(className, classLoader)
复制代码
  1. 直接经过指定实例进行建立:建立后clazz与instance均存在, clazz为instance.javaClass数据
val reflect = EasyReflect.create(any)
复制代码

了解了EasyReflect的几种建立方式与其区别后。咱们就能够正式进行使用了

3. 调用指定构造器进行实例建立

val reflect:EasyReflect = createReflect()
// 经过默认空构造器进行对象建立. 经过get方法获取建立出的对象
val instance = reflect.instance().get<Any>()
// 经过含参构造器进行对象建立
val instance2 = reflect.instance(arg0, arg1...argN).get<Any>()
复制代码

4. 字段的赋值与取值

EasyReflect对字段的操做,不用再去考虑它是不是静态的或者仍是final修饰的。更不用去管字段是不是private不可见的。咱们只须要指定字段名便可。这大大加强了便利性!

  • 访问指定字段的值:
val value:Any = reflect.getFieldValue<Any>(fieldName)
复制代码
  • 为指定字段赋值:
reflect.setField(fieldName, newValue)
复制代码
  • 使用指定字段的值建立新的EasyReflect实例进行使用
val newReflect = reflect.transform(fieldName)
复制代码

5. 方法调用

与字段操做相似,咱们也不用去担忧方法的可见性问题。须要的只有此方法存在便可

  • 调用指定方法
// 调用无参方法
reflect.call(methodName)
// 调用有参方法
reflect.call(methodName, arg0, arg1...argN)
复制代码
  • 调用指定方法并获取返回值
val value = reflect.callWithReturn(methodName, arg0, arg1...argN).get()
复制代码

6. 传参包括可变参数时的调用方式

可变参数会在编译时。替换为数组的形式。因此使用反射进行可变参数的传参时,须要使用数组的形式对参数进行构建:

如下方的两个方法为例:

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中不存在此类问题。

7. 使用动态代理进行托管

当你须要对某个类进行访问,可是又不想经过写死名字的方式去调用时,可使用此特性:

经过动态代理的方式,建立一个代理类来对反射操做进行托管

仍是经过举例来讲明:

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")// 方式二取值
复制代码

从某种程度上来讲:代理托管方案会比通常的直接反射操做要繁琐一点。可是带来的便利也是显而易见的:

首当其冲的优势就是:托管方式写的代码比直接进行反射操做更加优雅~

其次,在某些使用环境下,好比在团队协做中:能够将对外不方便直接提供实例的经过托管代理实例进行提供。

更多示例

为了方便你们更好的理解,这一节中我会列举一些小案例,方便你们更清楚的知道应该怎么去使用:

1. 使用指定构造器建立实例

假设咱们有如下一个类须要建立:

data class Example private constructor(val name: String)
复制代码

那么咱们能够这样来进行建立:

val example = EasyReflect.create(Example::class.java)// 指定实体类的class
	.instance("Hello World")// 根据对应的构造器的入参类型进行传参
	.get<Example>() // 获取建立好的实体类实例
复制代码

2. 对类中指定成员变量进行取值、赋值

假设咱们须要对此类中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")
复制代码

3. 对类中的静态变量进行取值、赋值

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")
复制代码

4. 访问成员方法

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类进行操做
...
复制代码

5. 访问静态方法:

静态方法也是隶属于类自己的,因此说:与静态变量的访问相似,只须要指定操做的类便可直接访问了:

val reflect = EasyReflect.create(Example::class.java)
复制代码

其余具体的操做方式与成员方法的操做均一致。

6. 使用指定Field的值建立新的EasyReflect实例提供使用

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数据。
复制代码

7. 使用动态代理进行托管管理

所谓的动态代理托管,便是经过动态代理的方式,将一个代理接口实体类进行绑定,使得能够经过操做绑定的代理类作到操做对应实体类的效果

1. 映射代理方法

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()
复制代码

2. 映射代理变量

固然,也能够对被代理实例中的变量进行代理,好比:

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
复制代码

但愿这些示例能让你们在使用时有所帮助~

欢迎扫描下方二维码关注个人公众号👇

相关文章
相关标签/搜索