Occasionally, while programming, one may want to create constant sets and store them in final variables for public use. Such a desire can lead to all sorts of problems.
Consider this example:
package com.sysgears.examples;
public class ClassicalElements {
public static final Set<String> ELEMENTS;
static {
ELEMENTS = new HashSet<String>();
ELEMENTS.add("Earth");
ELEMENTS.add("Water");
ELEMENTS.add("Air");
ELEMENTS.add("Fire");
}
}
Code language: JavaScript (javascript)
The goal of this code is to create a class with a Set of final and static elements. All the purpose of it is to have a Set that can be used without concerns about possibility of accidentally changing it. The problem is that this Set isn’t final at all! Breaking it is not a big deal:
public final static void breakMethod() {
Set<String> elemets = ClassicalElements.ELEMETS;
elemets.add("Metal"); // <= unnoticible logic error
//...
}
Code language: JavaScript (javascript)
The reference to the Set is final, but the Set itself is mutable. To say short, this constant variable isn’t very constant. The point is that final is not the same as immutable. Let’s firm up this Set with in the following way:
package com.sysgears.examples;
public class ClassicalElementsBetter {
public static final Set<String> ELEMENTS;
static {
Set<String> temp = new HashSet<String>();
temp.add("Earth");
temp.add("Water");
temp.add("Air");
temp.add("Fire");
ELEMENTS = Collections.unmodifiableSet(temp);
}
}
Code language: JavaScript (javascript)
This variant of the class is much better. The Set of elements cannot be modified because it has been turned into an immutable object. The reference to the Set is final, and the contents of the collection are locked down.
Note that it is necessary to use a temporary set to store the elements. This is because you can set a final variable only once, even in the static{} initialiser. If you try to set it more than once or change the variable in the initialiser, your compiler will give an error message stating that you cannot change the final variable. Now, with the strategy to lock down a final Set, let’s revisit the logic bug discussed in the previous example:
public final static void breakMethod() {
Set<String> elemets = ClassicalElementsBetter.ELEMETS;
elemets.add("Metal"); // <= exception here
//...
}
Code language: JavaScript (javascript)
Now, after locking down the Set, this code results in an exception. Specifically, the method will throw an UnsupportedOperationException whenever a user tries to use any write methods on ELEMENTS, as it is now immutable. Although this is not as prominent as compile time error, it is definitely better than nothing. You should always use the java.util.Collections to get inchangable collections and maps when creating final collections and maps.
Keep in mind that there is no similar way to lock down final array objects, so be careful when using them.