Java

The Final (Constants) Story

Generally, it is better to swap a logic error for a compiler error. Compiler errors can be found quickly and fixed just as quickly, whereas logic errors can take thouands of man-hours to find and fix. This article explains how to use the Java keyword final to change logic errors into compiler errors, thus saving you an enormous amount of time. It is excerpted from chapter two of Hardcore Java, written by Robert Simmons Jr. (O'Reilly, 2004; ISBN: 0596005687).

The goal of this code is to declare a class with aSetoffinalandstatic Colors representing the colors of the rainbow. You want to be able to use thisSetwithout concerning yourself with the possibility of accidentally changing it. The problem is that theSetisnítfinalat all! Break it with Example 2-12.

This version of the class is much better. YourSet ofColors cannot be modified because you have turned it into an immutable object. The reference to theSetisfinal, and the contents of the collection are locked down.

In thestatic{}initializer, note how you have to use a temporary set to store the colors. This is because you can set afinalvariable only once, even in the initializer. If you try to set it more than once or change the variable in the initializer, your compiler will give an error message stating that you cannot change thefinalvariable. Remember that deferredfinals are a one-shot deal. Once set (no pun intended), they canít be changed.

Now that you have a strategy to lock down your Set, letís revisit the old logic bug that we discussed in Example 2-12:

Now that you have theSetlocked down, this code results in an exception. Specifically, the method will throw anUnsupportedOperationExceptionwhenever the user tries to use any write methods onVALID_COLORS, as it is now immutable. In this case, you havenít been able to trade a logic bug for a compiler bug, but you have been able to trade a logic bug for an exception. Although this trade isnít as good, itís still definitely worthwhile. Always use thejava.util.Collectionsclass to get unmodifiable collections and maps when creatingfinalcollections and maps.

As far as unmodifiable sets go, the performance hit is negligible. As it turns out, the JDK implements unmodifiable collections in a performance-conscious way. If you look into the JDK source, you will see the static nested classesUnmodifiableSet andUnmodifableCollection. The code in Example 2-13* is pasted directly from the JDK source. All I did was change the spacing to conform to OíReilly standards and remove the Javadoc for brevityís sake.

When you callCollections.unmodifiableSet(), the class creates a new instance of this static nested class and sets the source collection as the delegate object. As you can see in the example code from the JDK, the classUnmodifiableSetimplementsjava.util.Setand inherits fromUnmodifiableCollection, which in turn implementsjava.util.Collection. Together, they form a delegate structure. Any read call to theUnmodifiableCollectionis forwarded to the delegate collection. However, if the user tries to access a write operation, the class throws an instance ofUnsupportedOperationException. Therefore, the additional overhead of theUnmodifiableSet is only a single method call.

This delegate structure also plugs another big hole: if theUnmodifiableSetclass inherited fromHashSet, then the user could just cast the instances back toHashSetto gain access to write methods. The delegate structure in the JDK quite elegantly blocks this, ensuring that anUnmodifiableSettruly is unmodifiable, even when placed in the hands of a clever and sneaky programmer.

All of the other collection classes work similarly toUnmodifiableSet. You should use these heavily in your code. Regrettably, there is no similar way to lock downfinalarray objects, so be careful when using them.