Java is statically typed and this pervades the design and code written in it (regardless of whether you think that is a good thing). If you try to subvert this using reflection, you are working against the language and losing the major benefits of static typing, such as the ability to refactor, compile-time type checking, etc.
Which leads me to a code smell that I’ve seen mentioned a couple times recently and resonated with me due to some stuff I’ve seen in code bases I’ve worked in. In Java, any time you are putting Java class (or more rarely method) names in strings, you are making type information invisible to the compiler, and thus losing the benefits of static typing.
That doesn’t mean you should never do this. But it does mean that it should set off red flags and alarms and the decision to use reflection and store class names in strings should be made very deliberately and with an understanding of the consequences. First preference is to avoid using reflection at all. Second preference is to instead use com.foo.Bar.class or bar.getClass().
One place where class names show up that I think is unavoidable (and worth the consequences) is the case of delayed loading. Sometimes, you want to use a plugin-like system and delay loading of classes, either because you want to dynamically choose dependencies or because you want to hide the loading of dependencies inside a custom classloader. In either case, you can’t refer directly to the class as a Class as that would cause it to be loaded at the point of reference.
I dimly recall running into a similar use case with method names when implementing a dynamic proxy at some point in the distant past that was intercepting only certain calls to a target, based on method name. Given that there is no .method equivalent to .class, I don’t think there is any way to avoid referencing the method by name in this case. The only weird idea I can think of is maybe to define an interface containing just the methods that you wanted to check by name, then do a check to see whether the incoming method name was in that other interface’s methods. Of course, the best idea would have been to avoid using the dynamic proxy in the first place, but it was unavoidable at the time.
There is an interesting tangent to this involving annotations, which I will blog separately.