前一段时间,我写了这个问题的答案,它关于如何避免每个可变变量都具有getter和setter方法。当时,我只有一种难以言表的直觉,认为这是一个坏主意,但OP明确要求这样做。我在这里搜索了这可能是个问题的原因,然后发现了这个问题,其答案似乎认为,除非绝对必要,否则使用反射是不好的做法。
那么,有人可以说出为什么应该避免反射的原因,特别是在通用getter和setter的情况下?
Map
的get
和put
,这是做事情的一个非常笨拙的方法。
前一段时间,我写了这个问题的答案,它关于如何避免每个可变变量都具有getter和setter方法。当时,我只有一种难以言表的直觉,认为这是一个坏主意,但OP明确要求这样做。我在这里搜索了这可能是个问题的原因,然后发现了这个问题,其答案似乎认为,除非绝对必要,否则使用反射是不好的做法。
那么,有人可以说出为什么应该避免反射的原因,特别是在通用getter和setter的情况下?
Map
的get
和put
,这是做事情的一个非常笨拙的方法。
Answers:
以我的经验,反射是Java中的“专家级”功能。我认为大多数程序员永远不会主动使用反射(即使用反射的库不算在内)。这使得使用这些代码的程序员更难理解。
假设getFoo
我班上有一个吸气剂,我想将其重命名为getBar
。如果我不使用反射,则可以在代码库中进行搜索,getFoo
并找到使用该getter的每个位置,以便我可以对其进行更新,即使我错过了它,编译器也会抱怨。
但是,如果使用吸气剂的地方是一样的东西callGetter("Foo")
和callGetter
做getClass().getMethod("get"+name).invoke(this)
,那么上面的方法不会找到它,编译器不会抱怨。只有在实际执行代码时,您才会获得NoSuchMethodException
。并想象一下,如果该异常(被跟踪)被吞噬了,您会感到痛苦,callGetter
因为“它仅与硬编码的字符串一起使用,实际上并不会发生”。(没有人会这样做,有人可能会争论?除了OP 在他的SO回答中确实做到了这一点。如果重命名该字段,则通用设置器的用户将永远不会注意到,除了设置器的极其晦涩的错误却无所事事。如果很幸运,getter的用户可能会注意到控制台输出的被忽略的异常。)
基本上,这是上述内容的重要内容。反射代码是关于Object
。在运行时检查类型。错误是通过单元测试发现的,但前提是您有覆盖范围。(“这只是吸气剂,我不需要对其进行测试。”)基本上,与使用Python相比,使用Java的优势首先使您失去了优势。
也许不是从理论上讲,但在实践中,您不会找到为内联或创建内联缓存的JVM Method.invoke
。常规方法调用可用于此类优化。这使它们更快。
反射代码所需的动态方法查找和类型检查比正常方法调用慢。如果您将廉价的一站式吸气剂转变为反射型野兽,您可能(我尚未测量过)正在观察几个数量级的减速。
那只是个坏主意,因为您的类现在不再具有封装。它具有的每个字段都是可访问的。您不妨将它们全部公开。
因为直通吸气剂/阻气剂是可憎的,没有价值。它们不提供任何封装,因为用户可以通过属性获得实际值。他们是YAGNI,因为您几乎永远不会更改现场存储的实现。
而且因为这会降低性能。至少在C#中,getter / setter将直接内联到单个CIL指令。在您的答案中,您将其替换为3个函数调用,这又需要加载元数据以进行反映。我通常使用10倍作为反映成本的经验法则,但是当我搜索数据时,第一个答案之一就是包含了该答案,并将其接近1000倍。
(而且这使您的代码更难调试,并且因为有更多的滥用方式而损害了可用性,并且由于您现在使用的是魔术字符串而不是适当的标识符,因此也损害了可维护性。)
getX()
和setX()
可以通过反射找到的方法公开字段。Bean不一定要封装它们的值,它们可能只是DTO。因此,在Java中,吸气剂通常是必要的痛苦。C#属性在各个方面都好得多。
除了已经提出的论点。
用户希望调用foo.getBar()和foo.setBar(value),而不是foo.get(“ bar”)和foo.set(“ bar”,value)
要提供用户期望的界面,您需要为每个属性编写单独的getter / setter。一旦完成,使用反射就没有意义了。