什么是EasyReflect

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

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

EasyAndroid作为一款集成组件库,此库中所集成的组件,均包含以下特点,你可以放心使用~~

1. 设计独立

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

2. 设计轻巧

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

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

得益于编码时的高内聚性,若你只需要使用EasyReflect. 那么可以直接去拷贝EasyReflect源码文件到你的项目中,直接进行使用,也是没问题的。

EasyAndroid开源库地址:

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:Userprivate 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 {@JvmStaticval 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
复制代码

希望这些示例能让大家在使用时有所帮助~

欢迎扫描下方二维码关注我的公众号?

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

  1. python英译汉库模块_翻译|Python标准功能库1

    上班的时候偷懒,把Python帮助里的标准功能库1过了一遍,顺便翻译了一下,虽然我知道基本没有人看,但不是说21世纪编程能力是基本的生存力嘛. 通过阅读本文,你将了解Python的11个标准功能库1. ...

  2. 【Android RTMP】Android Studio 集成 x264 开源库 ( Ubuntu 交叉编译 | Android Studio 导入函数库 )

    文章目录 安卓直播推流专栏博客总结 一. x264 简介 二. x264 交叉编译 三. Android Studio 导入函数库 四. 交叉编译版本 五. GitHub 项目地址 安卓直播推流专栏博 ...

  3. 中国移动oneos框架基础及其组件解析

    <关键字> 中国移动oneos .开发环境 .开机自启动. shell .单元测试. 源码分析 1.oneos系统 1.1 开发手册 OneOS是中国移动针对物联网领域推出的轻量级操作系统 ...

  4. 新一代医院信息系统(NGHIS)设计(2)——基础集成平台(I)

    一.背景介绍 基础集成平台是信息系统的基础设施环境,为各应用系统提供公共基础设施(如ESB.消息中间件等),将各系统的通用基础服务功能(如用户管理.授权管理.配置管理等)从业务系统剥离出来,使得业务系 ...

  5. 511遇见易语言基础命令组件和算法

    一.易语言基础命令组件 1:易语言教程-窗口 2:易语言教程-编辑框和按钮 3:易语言教程-信息框 4:易语言教程-数据类型 5:易语言教程-变量(全局变量,程序集变量,局部变量,静态变量) 6:易语 ...

  6. 【SeaTunnel】从一个数据集成组件演化成企业级的服务

    点亮 ⭐️ Star · 照亮开源之路 GitHub:https://github.com/apache/incubator-seatunnel 在 7 月 24 日 Apache SeaTunnel ...

  7. 五、Web App 基础可视组件属性(IVX 快速开发教程)

    五.基础可视组件属性 在 iVX 中各个组件存在不同的属性,这些属性用于设置显示的样式或者是自身具备的特征等,通过更改这些属性可以极大的方便我们进行项目的创作. 大多数组件都拥有相同的属性,相同属性在 ...

  8. 四、WebApp 基础可视组件(IVX 快速开发教程)

    四.基础可视组件 通过本节你将了解 iVX 开发中的核心-- iVX 组件的使用方法.iVX 的组件是开发应用时所必要的对象,通过这些对象你将快速的完成应用的开发. 在 iVX 应用开发中,所有交互. ...

  9. -组件基础-局部组件 // 局部组件的简写

    -组件基础-局部组件 <!DOCTYPE html> <html lang="en"><head><meta charset=" ...

  10. 组件基础-全局组件//全局组件的简写

    组件基础-全局组件 <!DOCTYPE html> <html lang="en"><head><meta charset="U ...

最新文章

  1. Too Many Segments CF595D 贪心乱搞
  2. pthread中如何追踪stack over flow
  3. oracle中判断是否为季末,Oracle中取月初,月末,季初,季末及年初,年末时间总结...
  4. java filter注入_如何使用Filter过滤请求中的SQL注入攻击
  5. 深层神经网络难以训练的原因
  6. /bin/bash^M: bad interpreter: No such file or directory
  7. 新一代企业级大数据应用方案
  8. 微信小程序背景音乐官方实例代码无效问题解决及音乐src获取方法
  9. java为纯图PDF添加图片水印
  10. 想查看实时卫星影像?最近一周就不错了
  11. matplotlib折线图(标记点、标记点大小、标记点边颜色、标记点边宽)
  12. 前东家欠款 1.4 亿?我看贝店爆雷事件
  13. 菜狗杯Misc迅疾响应wp
  14. 第02课:主流分布式缓存方案的解读及比较
  15. 仿选单网图片合成小工具
  16. 人工智能领域排名|按AI顶会评实力:美国7倍领先中国,谷歌雄霸全球第一,腾讯和清华分获中国产学No.1...
  17. 高效人士的七个好习惯
  18. 基于单片机的车速控制语音报警系统
  19. SparkSteaming实时接收网络端口数据和hdfs做Wordcount
  20. you ll just never know

热门文章

  1. 员工因上厕所时间超长被开除了,法院:超出正常生理需求范围!
  2. mysql 存储过程 树结构_mysql 树形存储过程
  3. mysql更改密码_Mysql更改密码、连接mysql、mysql常用命令
  4. java的path含义_java中path和CLASSPATH的配置和意义解析
  5. python配置文件模块_Python解析配置文件模块:ConfigPhaser
  6. easypoi 语法_知识点总结及语法学习资料及视频
  7. 计算机语言em,Python:EM(期望极大算法)实战
  8. 被替换的项目不是替换值长度的倍数_机器学习中处理缺失值的9种方法
  9. excel 未能加载文件或程序集 officetoolbar_JavaScript数据透视表dhtmlxPivot,帮助您方便快捷的汇总大型数据集...
  10. java 带参数转发_Nginx 根据URL带的参数转发的实现