Tuesday, August 23, 2011

Don't use String literals for Reflection

Do you know what this line of code is for?

@OneToMany(mappedBy = "assignedInvoice")
private List<PurchaseOrderAssignment> purchaseOrders;

And this one?


Or this one?

Field nameField = Person.class.getField("name");

Ok, not so difficult.
First one is a Hibernate annotation for mapping a one-to-many relationship indicating wich field is the owner. The second one is how you obtain a reference to a class Object for String class. And the third one is how you obtain the "name" field of the class "Person" using reflection.
If you have been around Java long enough you should be familiar with of these forms of reflection.

Now. Can you tell what's wrong with them? (hint: the tittle of this article can help you)
Yes! There are using string literals to refer to class structure elements.

And why is that wrong?
Because doing so you loose the relationship with the referred element. Your code looses the information about structure dependencies.
Using string literals you cannot know where all the reflections references to some class field are, prior to a refactor.You cannot change the name of the field in one place and expect to automatically update all the references (remember DRY?).

In other words you are making implicit an existing dependency relationship between referred and referral code and you start to loose control over changes.

What should I do then?
Until the RFE(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5043025) is implemented in the JDK, we will have to content ourselves with String constants.
We cannot honor the DRY principle, but at least we can repeat ourselves just once.

I propose you to declare a constant with the name of the field, or the class, or whatever you may need to refer using reflection, very near to the place where it is declared.

As an example to the one-to-many relationship counterpart:

  private Invoice assignedInvoice;
  public static final String assignedInvoice_FIELD = "assignedInvoice";

Here I declare the field and below the constant to refer to it with reflection.
I then, use that constant (assignedInvoice_FIELD) to refer to the field using any form of reflection (annotations too).

@OneToMany(mappedBy = PurchaseOrderAssignment.assignedInvoice_FIELD)
private List<PurchaseOrderAssignment> purchaseOrders;

The sad part is that you have to update the constant every time you rename the field (or class), but luckily you won't be changing that too much and in case you do, you will have to change just one place.
The nice part is that now, you can see where all your reflections references are with most IDEs and if you happen to delete or rename the field carelessly, having that constant near is a reminder of the existing dependencies.

Of course this is not an ideal solution, but is good enough to keep you in control of your code.

Extra note
I prefer to use "_FIELD" as a suffix when the constant refers a field so that the IDE auto-completion capabilities offers me the constant and I know that is not a getter or setter method. I use "_GETTER" suffix when the property is not available as a field on the class.

Other interesting articles

No comments:

Post a Comment