Home arrow Java arrow Page 4 - The Final (Constants) Story
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).

Author Info:
By: O'Reilly Media
Rating: 4 stars4 stars4 stars4 stars4 stars / 8
October 20, 2005
TABLE OF CONTENTS:
  1. · The Final (Constants) Story
  2. · Excessive Constants
  3. · Deferred Initialization
  4. · Final Collections
  5. · Instance-Scoped Variables
  6. · Final Methods
  7. · Conditional Compilation Variable Location

print this article
SEARCH DEVARTICLES

The Final (Constants) Story - Final Collections
(Page 4 of 7 )

Periodically, while programming, you may want to make constant sets and store them in final variables for public use. This desire can lead to all sorts of problems. Consider the code in Example 2-11.

Example 2-11. A collection in a final static member

package oreilly.hcj.finalstory;
public class FinalCollections {
 
public static class Rainbow {
   
public final static Set VALID_COLORS;
   
static {
       VALID_COLORS = new HashSet(); 
      VALID_COLORS.add(Color.red);
      VALID_COLORS.add(Color.orange);
      VALID_COLORS.add(Color.yellow);
      VALID_COLORS.add(Color.green);
      VALID_COLORS.add(Color.blue);
      VALID_COLORS.add(Color.decode("#4B0082")); // indigo
      VALID_COLORS.add(Color.decode("#8A2BE2")); // violet
    
}
  }
}

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.

Example 2-12. A defect caused by a nonimmutable set

package oreilly.hcj.finalstory;
public final static void someMethod() {
  Set colors = Rainbow.VALID_COLORS;
 
colors.add(Color.black); // <= logic error but allowed by compiler
  System.out.println(colors);
}

The reference to theSet is final, but theSet itself is mutable. In short, your constant variable isnít very constant. The point is thatfinalis not the same as immutable.

You can firm up this code in the same way you locked down returned collections from a bean in Chapter 1:

package oreilly.hcj.finalstory;
public static class RainbowBetter {
    
public final static Set VALID_COLORS;
   
static {
       Set temp = new HashSet();
      temp.add(Color.red);
      temp.add(Color.orange);
      temp.add(Color.yellow);
      temp.add(Color.green);
      temp.add(Color.blue);
      temp.add(Color.decode("#4B0082")); // indigo
      temp.add(Color.decode("#8A2BE2")); // violet
      VALID_COLORS = Collections.unmodifiableSet(temp);

    }
  }
}

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:

package oreilly.hcj.finalstory;
public final static void someMethod() {
  Set colors = RainbowBetter.VALID_COLORS;
 
colors.add(Color.black); // <= exception here
 
System.out.println(colors);
}

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.

Example 2-13. Implementation of unmodifiable collections

package oreilly.hcj.finalstory;
public static Collection unmodifiableCollection(Collection c) {
  return new UnmodifiableCollection(c);
}
static class UnmodifiableCollection implements Collection, Serializable {
  // use serialVersionUID from JDK 1.2.2 for interoperability
  private static final long serialVersionUID = 1820017752578914078L;
 
Collection c;
 
UnmodifiableCollection(Collection c) {
    if (c==null)
       throw new NullPointerException(); 
    this.c = c;
  }
 
public int size()      {return c.size();}
  public boolean isEmpty() {return c.isEmpty();}
  public boolean contains(Object o)  {return c.contains(o);}
  public Object[] toArray()   {return c.toArray();}
  public Object[] toArray(Object[] a) {return c.toArray(a);}
  public String toString()  {return c.toString();}
 
public Iterator iterator() {
    return new Iterator() {
      Iterator i = c.iterator();
   
public boolean hasNext() {return i.hasNext();}
      public Object next() {return i.next();}
        public void remove() {
          throw new UnsupportedOperationException();
      }
    };
  }
  public boolean add(Object o){
    throw new UnsupportedOperationException();
  }
 
public boolean remove(Object o) {
    throw new UnsupportedOperationException();
  }
 
public boolean containsAll(Collection coll) {
    return c.containsAll(coll);
  }
 
public boolean addAll(Collection coll) {
    throw new UnsupportedOperationException();
  }
 
public boolean removeAll(Collection coll) {
    throw new UnsupportedOperationException();
  }
 
public boolean retainAll(Collection coll) {
    throw new UnsupportedOperationException();
  }
 
public void clear() {
    throw new UnsupportedOperationException();
  }
}
public static Set unmodifiableSet(Set s) {
  return new UnmodifiableSet(s);
}

* From JDK source Java.util.Collections.

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.


blog comments powered by Disqus
JAVA ARTICLES

- Java Too Insecure, Says Microsoft Researcher
- Google Beats Oracle in Java Ruling
- Deploying Multiple Java Applets as One
- Deploying Java Applets
- Understanding Deployment Frameworks
- Database Programming in Java Using JDBC
- Extension Interfaces and SAX
- Entities, Handlers and SAX
- Advanced SAX
- Conversions and Java Print Streams
- Formatters and Java Print Streams
- Java Print Streams
- Wildcards, Arrays, and Generics in Java
- Wildcards and Generic Methods in Java
- Finishing the Project: Java Web Development ...

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials