Usually I don’t write about language specific things, but today’s post is an exception. It’s purely Java. Also, this post is not about clean code or something, just an opinion. You either take it as an advice, or just forget about these things; it’s up to you.
I understand that Java Reflection is a terribly useful API, and I don’t say by any means that we should stop using it. One should definitely be using it when developing framweorks that can accept different kinds of plug-ins. In such situations, reflection can be our best friend. However, it should be used with care.
Now you may wonder; what is wrong with Reflection? Well, nothing in particular, however, it has one terrible drawback: reflection based dependencies are not physical. They can only be found out at runtime. You break something and the code will still compile. Your IDE’s “find references” feature just becomes useless.
Some time ago I was fighting a bug. It wasn’t a very tough one; to the contrary, it was easy enough to solve. After finishing the fix itself, I started cleaning up the class I was working on. It had a method (of public visibility) with apparently no references to it. I was quite sure about it being useless, as I use Eclipse and checked the code with the find references option. There were no results, so I gladly deleted that method. I even felt good: I deleted some dead code, made te system better, reduced its complexity etc.
I like to feel safe all the time so I run all the test cases, just to be sure. All of them were passing. I knew the code coverage wasn’t that high, but the green line filled me with confidence. It did, until I was doing a bring-up test. The whole application broke down: our project’s command line interface was not accepting a very important command anymore.
First, I didn’t get it. I had to browse through the code really thoroughly. And that was the point when I found out that the method was called through a reflection based method. I had to put the freshly deleted method back (with tears in my eyes).
Then it happened to me again (maybe a bird is learning faster than me, but anyway…). I was fixing a bug report written against a class which was supposed to be a Singleton. However, the singleton class was not thread safe. After fixing the problem, the class still had a static variable – again no references to it. I deleted the stuff again, run the test cases and BANG:
at java.lang.Class.getDeclaredField(Unknown Source)
Yes, a reflection based test case. And that’s why one should not write reflection based test cases under any circumstances. They are fragile, error prone and kind of useless in an agile environment (where constant refactoring is in focus). There’s no way of knowing whether you mess something up with a simple rename process or not. There are enough scary things out there, one should not be afraid of renaming methods.
The same is true for methods. Let’s say it’s given a high level method, which calls a lower level private method, which again calls another private method and so on. One should not use reflection in order to test the lowest level (or any low-level) method. A much better approach would be to test the entry point instead, mock, stub or fake anything that is outside of your class under test, and build a reliable test case. Or, another thing you could do, make the low-level private method protected. Such a way you can be sure that the refactoring steps will not mess anything up. Or at least they’ll be immediately visible, and will not be hidden behind hardcoded string identifiers.
So, that is my view on reflection based programming; as I’ve already mentioned this is an opinion only.