Home arrow Java arrow 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
(Page 1 of 7 )

One fundamental principle of programming is that, generally, it is best to swap a logic error for a compiler error. Compiler errors tend to be found in seconds and are corrected just as fast. Syntax errors are a good example. A missing semicolon can make things confusing. If the compiler error is something particularly cryptic, the resolution may take as long as a couple of minutes to discover.

Logic errors, on the other hand, are the bane of all programmers. They hide and hate to reveal themselves. Logic errors seem to have minds of their own, constantly evading detection and dodging your efforts to pin down their cause. They can easily take a thousand times more effort to solve than the worst compiler errors. Worst of all, many logic errors are not found at all and occur only intermittently in sensitive places, which causes your customers to scream for a fix. Logic errors often require you to throw thousands of man-hours at them, only to finally discover that they are minor typos.

The Java keywordfinalcan be instrumental in turning thousands of logic errors into compiler errors without too much effort. With some training in coding standards and some code retrofitting, you can save an enormous amount of man-hours that are better spent elsewhere. Also, you can save your support departments from having to deal with irate customers.

Final Constants

Final constants are a good place to start, since many of you are already familiar with the concept. Consider the code in Example 2-1.

Example 2-1. A class that doesnít use constants

package oreilly.hcj.finalstory;
public class FinalConstants {
  public static class CircleTools {
   
public double getCircleArea(final double radius){
      return (Math.pow(radius, 2) * 3.141);
    }
   
public double getCircleCircumference(final double radius) {
      return ((radius * 2) * 3.141);
    }
   
public double getCircleExtrudedVolume(final double radius,
final double height) {
      return ((radius * 2 * height) * 3.141);
    }
  }
}

The problem with this code is that the developer has to change all three instances of the value3.141, his estimate for π, in all three methods if he wants to make his calculations more precise. Seasoned developers will see the opportunity for a class-scoped constant, as seen in Example 2-2.

Example 2-2. Simple constants using final

package oreilly.hcj.finalstory;
public class FinalConstants {
  public static class CircleToolsBetter {
   
/** A value for PI. **/
    public final static double PI = 3.141;
    public double getCircleArea(final double radius){
      return (Math.pow(radius, 2) * PI);
    }
   
public double getCircleArea(final double radius) {
       return ((radius * 2) * PI);
    }
   
public double getCircleExtrudedVolume(final double radius,
final double height) {
      return ((radius * 2 * height) * PI);
    }
  }
}

This code is much better. Now the developer can change the constant, and this one change will propagate throughout the class. The reason I am beating this particular dead horse is because there are some traps involving constants that trip up even experienced developers.

Public Primitives and Substitution

The first of these traps involves public primitive constants that are used by other code. Because primitive finals are substituted at compile time with their values, if you change a final that is used by other classes, you must remember to recompile those other classes or your change will not take effect. The same rule applies to constants of type java. lang. String. Although String is a constructed type, it is also substituted at compile time. All constructed types other than String, mutable or not, are not substituted at compile time. To understand how this works, look at Example 2-3.

Example 2-3. Various final variables

package oreilly.hcj.finalstory;
public class FinalReplacement {
  /** A string constant */
  public final static String A_STRING = "Hardcore Java";
 
/** An int constant. */
  public final static int AN_INT = 5;
 
/** A double constant. */
  public final static double A_DOUBLE = 102.55d;
 
/** An array constant. */
  public final static int[] AN_ARRAY = new int[] {1, 2, 3, 6, 9, 18, 36};
 
/** A color constant. */
  public final static Color A_COLOR = new Color(45, 0, 155);
 
public void someMethod() {
    System.out.println(A_STRING); 
    System.out.println(AN_INT);
    System.out.println(A_DOUBLE);
    System.out.println(AN_ARRAY);
    System.out.println(A_COLOR);
 
}
}

Once the compiler sees code such as this, it starts substituting out the primitives andString objects. After the first pass of the compiler, the class will look something like this:

package oreilly.hcj.finalstory;
public class FinalReplacement {
  /** A string constant */
  public final static String A_STRING = "Hardcore Java";
 
/** An int constant. */
  public final static int AN_INT = 5;
 
/** A double constant. */
  public final static double A_DOUBLE = 102.55d;
 
/** An array constant. */
  public final static int[] AN_ARRAY = new int[] {1, 2, 3, 6, 9, 18, 36};
 
/** A color constant. */
  public final static Color A_COLOR = new Color(45, 0, 155);
 
public void someMethod() {
    System.out.println("Hardcore Java");
    System.out.println(5);
    System.out.println(102.55d);
    System.out.println(AN_ARRAY);
    System.out.println(A_COLOR);
  }
}


The compiler will concatenate consecutiveStringliterals to form one literal. Therefore, the following two lines are identical from the point of view of the compiler:

public final static String A_STRING = "Hardcore Java";
public final static String A_STRING = "Hardcore"+ "Java";

Both of these lines would result in an identical declaration that is a string constant. Also, this optimization technique applies to where there are consecutive string literals in your code.


The primitive andStringconstants were substituted while the other constructed types were left as variables. Since this code is all in one class, if you change a constant, you have to recompile this class anyway. However, if another class (for example,ExternalUser) is using the constantA_STRINGand you change it inFinalReplacement, you have a problem.ExternalUser will have to be recompiled to trigger a resubstitution using the newA_STRINGvalue, but the Java compiler will not notice this dependency. Hereís a simple version ofExternalUser:

package oreilly.hcj.finalstory;
public class ExternalUser {
  public static void main(String[] args) {
    System.out.println("The title of the book is: " +                    
             FinalReplacement.A_STRING
+ ".");
  }
}

This extremely simple class uses theA_STRINGconstant from theFinalReplacementclass. If you run themain()method, the output will look like the following:

>ant -Dexample=oreilly.hcj.finalstory.ExternalUser run_example
run_example:
     [java] The title of the book is: Hardcore Java.

Now change the value ofA_STRINGto"Java Hardcore"in theFinalReplacementclass:

/** A string constant */
  public final static String A_STRING = "Java Hardcore";

RecompileFinalReplacementusing the following command:

>ant -Dexample=oreilly/hcj/finalstory/FinalReplacement.java compile_example
compile_example:
    
[javac] Compiling 1 source file to C:devhcjbin

Now run theExternalUserexample again:

>ant -Dexample=oreilly.hcj.finalstory.ExternalUser run_example
run_example:
    
[java] The title of the book is: Hardcore Java.

There is no change despite the change in theA_STRINGconstant. To fix this problem, recompile theExternalUserclass:

>ant -Dexample=oreilly/hcj/finalstory/ExternalUser.java compile_example
compile_example:
   
[javac] Compiling 1 source file to C:devhcjbin

Running the example once more gives you the output you were seeking when you changedA_STRING inFinalReplacement:

>ant -Dexample=oreilly.hcj.finalstory.ExternalUser run_example
run_example:
   
[java] The title of the book is: Java Hardcore.

The Java compiler doesnít automatically notice the dependency between theStringand primitive constants and their users. Some build environments recognize this dependency automatically but donít depend on it.

In fact, whenever you change a public primitive constant, itís a good idea to simply rebuild the whole project, just to be safe. If your code is also being used by other projects, make sure you put the change in your release notes so that others will know to recompile their projects.


During a project for the aerospace industry, my consulting company changed a primitive public constant and forgot to change the release notes. We promptly broke a customerís code and only hours later did we figure out that they needed to rebuild all 400 of their classes to get it working.



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