The Dangers of Reflection: Or, Put Down That Mirror!
Thursday, January 11th, 2007I want to start out by saying that reflection in itself isn’t a Bad Thing™. It is just one of those things that tends to be oft abused. I should know, since I’ve abused it more often than I care to admit.
To put things in perspective, let me list some examples of well designed projects that use reflection.
- JUnit
- JUnit uses reflection to ease the creating of individual tests. A test writer can add a test simple by creating a public void method with a name that starts with “test”. This allows you to write many distinct tests without having to “wire up” the tests manually. They get executed by virtue of naming convention.
- The Spring Framework
- The Spring Framework is actually a collection of many different “tools”. Its guiding principal is to help software engineer better design thier systems to use the Inversion of Control pattern. There are many different parts of Spring that use reflection to aid with IoC, so I’ll just name one example. Spring allows you to define an application context in XML. The application context allows you to inject dependencies, so that class implementations don’t have to care about where their dependencies come from. Its almost obvious that reflection is the cleanest way to handle any java object in abstract ways. Without reflection, you would have to litter your code with Spring specific interfaces and implementation details. Ick!
- Hibernate
- Hibernate is used to map data between object models and relational models. In other words, its used as the bridge between Java and SQL. The latest versions of Hibernate allow you to create your database structure based on your JavaBean classes. It uses reflection (and proxies) to automate translation to and from relational data.
There are a few more well-designed projects that use reflection, but lets look at what seems to be a common thread.
JUnit is a general-purpose testing framework. Spring is a general-purpose IoC framework. Hibernate is a general-purpose ORM framework.
See a pattern?
Reflection is well suited for general purpose frameworks. Its used when polymorphism just won’t cut the bill.
So, what abuses of reflection have I seen? I have to admit, and I’m ashamed, they are mostly ones I’ve committed.
—-
One of the more “interesting” classes I’ve created is the DelegateMap. The DelegateMap implements java.util.Map, and allows you to “connect” a bean object to it. This means the map becomes a view of the bean. if you call map.put(”propertyName”, value), it will actually update the bean. if you call map.get(”propertyName”), it will return the value held by the bean’s property. Sound like a useful gizmo? I won’t say it isn’t useful, but it is a bit more error prone than I would have liked.
map.putAll(otherMap) is an interesting use of this class. At first glance, it looks like you can copy one bean to another, without worrying about individual properties. assert !book.getCover().judged(); An interesting note about the JavaBean spec, is that by default, all objects have a property called “class” {see Object.getClass()}. This property is read-only (imagine trying to implement strong-typing in an environment where the type can change at will!). delegateMap.putAll(otherDelegateMap) will throw an exception when it tries to set the read-only property “class”.
The other danger of this class is that it breaks refactoring. Simple automatic refactoring, such as IntelliJ IDEA’s “rename”, should change the source of a project without changing the behavior. If you have two objects that aren’t related other than through naming convention on the property name, it becomes conceivable to break a “delegate map copy operation” simply by changing the name of one property in either class. Whats worse is your compiler will not notice, and your tests may not warn you in time.
—-
I’ve found myself refactoring away from introspection and reflection time after time. Inversion of Control is my usual destination pattern. Using polymorphism over reflection, I find that my design is cleaner and my code more easily understood. I also find that I have far fewer exceptions in my log files.
If I may make an analogy, reflection is a high-temperature cutting-torch, and we usually just need to cut some paper. Use with caution, and wear some safety goggles.
