Java Reflection – Why Generic Setters and Getters Are a Bad Idea

javareflection

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?

Best Answer

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.