49

A while back I wrote this answer to a question on how to avoid having a getter and setter for every mutable variable. At the time, I had only a hard-to-verbalize gut feeling that this is a Bad Idea, but OP was explicitly asking how to do it. I searched here on why this might be a problem, and found this question, whose answers seem to take it as a given that using reflection unless absolutely necessary is bad practice.

So, can someone verbalize why it is a given that reflection should be avoided, specifically in the case of the generic getter and setter?

  • 12
    If you just want to reduce boilerplate, both Lombok and Groovy will generate getters and setters silently but safely. – chrylis Oct 9 '17 at 19:01
  • 2
    How would you even consume these getters and setters in a static language like java? It seems like a non-starter to me. – Esben Skov Pedersen Oct 10 '17 at 6:32
  • @EsbenSkovPedersen You'd consume them much like a Map's getand put, which is a pretty clunky way of doing things. – HAEM Oct 10 '17 at 7:00
  • 2
    IIRC, Lombok synthesizes getters/setters at compile time, as dictated by the appropriate annotation, so they: don't incur the performance penalty of reflection, can be statically analyzed/inlined, can be refactored (IntelliJ has a plugin for Lombok) – Alexander Oct 10 '17 at 19:40
116

Downsides of reflection in general

Reflection is harder to understand than straight-line code.

In my experience, reflection is an "expert-level" feature in Java. I would argue that most programmers never use reflection actively (i.e. consuming libraries that use reflection doesn't count). That makes code using it harder to understand for these programmers.

Reflection code is inaccessible to static analysis

Suppose I have a getter getFoo in my class and I want to rename it to getBar. If I use no reflection, I can just search the code base for getFoo and will find every place that uses the getter so I can update it, and even if I miss one, the compiler will complain.

But if the place that uses the getter is something like callGetter("Foo") and callGetter does getClass().getMethod("get"+name).invoke(this), then the above method won't find it, and the compiler won't complain. Only when the code is actually executed will you get a NoSuchMethodException. And imagine the pain you're in if that exception (which is tracked) is swallowed by callGetter because "it's only used with hard-coded strings, it can't actually happen". (Nobody would do that, someone might argue? Except that the OP did exactly that in his SO answer. If the field is renamed, users of the generic setter would never notice, except for the extremely obscure bug of the setter silently doing nothing. Users of the getter might, if they're lucky, notice the console output of the ignored exception.)

Reflection code is not type-checked by the compiler

This is basically a big sub-point of the above. Reflection code is all about Object. Types are checked at runtime. Errors are discovered by unit tests, but only if you have coverage. ("It's just a getter, I don't need to test it.") Basically, you lose the advantage using Java over Python gained you in the first place.

Reflection code is unavailable for optimization

Maybe not in theory, but in practice, you won't find a JVM that inlines or creates an inline cache for Method.invoke. Normal method calls are available for such optimizations. That makes them a lot faster.

Reflection code is just slow in general

The dynamic method lookup and type checking necessary for reflection code is slower than normal method calls. If you turn that cheap one-line getter into a reflection beast, you might (I have not measured this) be looking at several orders of magnitude of slowdown.

Downside of generic getter/setter specifically

That's just a bad idea, because your class now has no encapsulation anymore. Every field it has is accessible. You might as well make them all public.

  • 8
    You hit all the major points. Pretty much a perfect answer. I could quibble that getters and setter are marginally better than public fields but the bigger point is correct. – JimmyJames Oct 9 '17 at 15:17
  • 2
    It's also worth mentioning that getters/setters can easily be generated by an IDE as well. – stiemannkj1 Oct 9 '17 at 19:02
  • 26
    +1 Debugging reflection logic in a production-size project should be recognized as torture by the UN and illegalized. – errantlinguist Oct 9 '17 at 20:55
  • 4
    "harder to understand for these programmers." - for these programmers it's significantly hard to understand, but it's harder to understand for everyone, no matter how proficient. – BartoszKP Oct 9 '17 at 21:20
  • 4
    "Reflection code is inaccessible to static analysis" - This. So important for enabling refactoring. And as static-analysis tools get more powerful (e.g. code search in the latest Team Foundation Server), writing code that enables them becomes even more valuable. – Dan J Oct 10 '17 at 4:57
11

Because pass-through getter/setters are an abomination that provide no value. They don't provide any encapsulation since users can get at the actual values via the properties. They are YAGNI because you're rarely if ever going to change the implementation of your field storage.

And because this is way less performant. In C# at least, a getter/setter will inline straight to a single CIL instruction. In your answer, you're replacing that with 3 function calls, which in turn need to load metadata to reflect over. I've generally used 10x as the rule of thumb for reflection costs, but when I searched for data, one of the first answers included this answer which ballparked it closer to 1000x.

(And it makes your code harder to debug, and it harms usability because there's more ways to misuse it, and it harms maintainability because you're now using magic strings rather than proper identifiers...)

  • 2
    Note that the question is tagged Java, which has such delightful crap like the Beans concept. A bean has no public fields, but it may expose fields via getX() and setX() methods that can be found via reflection. Beans aren't necessarily supposed to encapsulate their values, they might just be DTOs. So in Java, getters are often a necessary pain. C# properties are much, much nicer in every respect. – amon Oct 9 '17 at 15:03
  • 11
    C# properties are dressed-up getters/setters, though. But yes, the Beans concept is a disaster. – Sebastian Redl Oct 9 '17 at 15:07
  • 6
    @amon Right, C# took a terrible idea and made is easier to implement. – JimmyJames Oct 9 '17 at 15:15
  • 5
    @JimmyJames It's not a terrible idea, really; you can maintain ABI compatibility despite code changes. A problem many don't have, I suppose. – Casey Oct 9 '17 at 19:58
  • 3
    @Casey The scope of this goes beyond a comment but building an interface from the internal details of the implementation is backwards. It leads to a procedural design. There are times when having a getX method is the right answer but it's pretty infrequent in well-designed code. The problem with getters and setters in Java is not the boilerplate code around them, it's that they are part of a terrible design approach. Making that easier is like making it easier to punch yourself in the face. – JimmyJames Oct 9 '17 at 20:25
8

In addition to the arguments already presented.

It doesn't provide the expected interface.

Users expect to call foo.getBar() and foo.setBar(value), not foo.get("bar") and foo.set("bar",value)

To provide the interface that users expect you need to write a seperate getter/setter for each property. Once you have done that there is no point in using reflection.

  • Seems to me that this reason is more important than all the others in other answers. – Esben Skov Pedersen Oct 10 '17 at 15:14
-4

Of course is not a bad idea, it is just than in a normal java world is a bad idea(when you only wants a getter or setter). For example some frameworks(spring mvc) or librares already use it(jstl) in that way, some json o xml parsers are using it, if you want to use a DSL you are going to use it. Then it depends on your use case scenario.

Nothing is right or wrong as a fact.

protected by gnat Oct 10 '17 at 15:04

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.