开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
为什么每个人都告诉你,你不应该使用反射,但你看到它到处都是? 许多著名的、可靠的、受人喜爱的开源库都是基于Java反射的。 什么时候使用Java反射是好的和聪明的?什么时候是糟糕和危险的?开发人员应该遵循哪些规则,以免在使用反射时陷入混乱?
- 不要使用反射来违反其他开发人员设置的限制。
这是所有规则中最重要的。如果您正在使用来自另一个开发人员的一段代码,您将按照该开发人员的意图获得代码。特别是如果它是已发布API的一部分。
Final关键字——结束的字段、方法或类
私有、受保护或包私有(无修饰符)-限制访问
这些都是不应该用反射来修改的。
如果库的创建者希望您更改一个字段或重写一个方法,他/她不会添加最终修饰符。在特定的架构决策背后有一些原因。有可能无法访问的代码只有在特定的条件下才能正常工作。
“违反”公共API契约可能会对代码的其他部分产生负面影响,而这些部分您可能不知道。
看起来,您对公共api契约的“违反”没有任何负面影响,但您可能错了,因为您可能对库的理解不够透彻。
- 任何公共API之外的东西都是禁止的。
这直接源自上一点,但有不同的理由。
如果某些东西不是公共API的一部分,它可以在任何时候被更改。
图书馆的作者可以在任何时候更改没有“向世界公开”的内容,而不必给它发信号。如果依赖于只能在库内部使用的东西,那么只需更新到更新的版本,代码就会崩溃。内部代码可以在不同版本之间更改。
最糟糕的是,直到代码到达在运行时使用Reflection的部分时,您才会知道代码会失败。编译将会成功,没有任何问题。
- 仅供偶尔使用。
就性能而言:反射的使用可能从代价略高到“我的设备上发生了什么”代价高。
如果使用反射,总是会有一些性能开销。即使你认为现代vm在最小化这个问题上做得更好,它们也不能缓解这个问题。它们只是让它不那么痛苦。
考虑反射是可以的,如果您的代码需要偶尔运行。只是偶尔而已。不是每秒7124次。
- 除非你没别的办法。
一般来说:
如果你可以用“常规java方式”做一些事情,就不要使用反射。不值得。
首先,常规的方式要简单得多。其次,使用反射很容易出错(例如,你必须高度依赖“Strings”)。
为了帮助你更好地理解我的意思,这里是“常规java方式”调用的简单方法
- 下面是使用反射的相同情况:
尽管这是一个微不足道的示例,但它已经显示了许多问题。
您想要使用Java Reflection做的任何事情都需要大量代码。
如果someMethod名称更改,则在运行代码之前代码不会失败。编译将成功(参见:“禁止任何公共API以外的内容”一节)。
如果你身边有适合工作的工具,你就去使用它们。没必要自己锻造工具。
对于极少数超级专门化的情况,可以考虑使用Java反射(例如,参见反射桥方法与生成的代码通信)。这些通常都是基于Java反射从头开始设计的。
老实说,如果您甚至在考虑Java反射是否适合您的情况,那么可能有更好的方法来实现它。
- 任何反射用法都必须进行大量测试。
如果您分析了前面所有的要点,并且仍然相信Java反射是您的正确选择,那么您需要记住:
任何Java反射的使用都必须在测试中覆盖。
这对于减少由于Java反射的所有细节而可能产生的问题非常重要。它主要是关于最小化反射调用中使用的名称(例如方法)更改所引起的问题的可能性。通常情况下,当您的代码到达反射调用的点时,在运行时这将被“发现”(😉除外)。您的测试需要在交付代码之前防止这种情况的发生。