Home arrow Java arrow Page 2 - 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 - Excessive Constants
(Page 2 of 7 )

The overuse of class-scoped constants is another common trap that can clutter up otherwise well-built code. For example, consider a situation in which there are many constants in a mathematically oriented class, as shown in Example 2-4.

Example 2-4. Excessive use of private constants

package oreilly.hcj.finalstory;
public class FinalConstants {
  public class SomeClass {
    /** Contains the constant for the first equation. */
    private static final double K1 = 3.141;
    
/** Contains the offset for the first equation. */
    private static final double X1 = 15.0;
   
/** Contains the constant for the second equation. */
    private static final double K2 = 1.414;
   
/** Contains the offset for the second equation. */
    private static final double X2 = 45.0;
    
/** Contains a constant for both equations. */
    private static final double M = 9.3;
    
public double equation1(final double inputValue) {
      return (((Math.pow(inputValue, 2.0d) / K1) + X1) / M);
    }
    
public double equation2(final double inputValue) {
      return (((Math.pow(inputValue, 3.0d) * K2) + X2) * M);
    }
  }
}

Although there is nothing technically wrong with this code, it is a rather nasty mess. If your equations become large, with multiple constants and numerous terms, the situation turns into something more appropriate for a horror movie. To avoid this trap, rewrite this disaster:

package oreilly.hcj.finalstory;
public class FinalConstants {
 
public class SomeClassBetter {
    /** Contains a constant for both equations. */
    private static final double M = 9.3;
   
public double equation1(final double inputValue) {
     
final double K = 3.141;
      final double X = 15.0;
      
return (((Math.pow(inputValue, 2.0d) / K) + X) / M);
    }
    public double equation2(final double inputValue) {
     
final double K = 1.414;
     final double X = 45.0; 
     re
turn (((Math.pow(inputValue, 3.0d) * K) + X) * M);
    }
  }
}

Although the method-scopedfinalvariables may look strange, they are quite legal and useful. For the compiler, the semantics for method-scoped constants are the same as those for class-scoped constants. The compiler will replace primitive andStringconstant variables with the value of the variable at compile time.

In fact, since the constants in Example 2-4 are private, you know that they cannot be accessed outside of the class, so there is no reason to leave them in the scope of the class. In the process of making your code easier to read and understand, you removed the need for those silly1s and2s in your constant names. Since the constants are used only in those methods, it is appropriate to restrict them to the methods in which they are used.


Take Advantage of Warning Options in Good Development Tools

Good tools can really help you to identify private variables and mistakes associates with them. For example, my IDE is configured to warn me if a private member of a class is not used in that class. Not only does this help me find private constants that should have been declared public, it also helps me clean up code that has been developed for a while and may have accumulated some lint throughout the process. Eclipse, for example, has many of these options; you should turn on all of them to warning level. Using these warnings can save you a lot of debugging time down the road.


 

To summarize, if you have aprivate final staticthat is used only in one method, you should probably move it into that method. On the other hand, if the constants are being used in more than one method, you should leave them as class-scoped. The constantMwas not moved because it was being used by two of the methods.

Final Variables

While we are on the subject of scoped final variables, you should keep in mind that these variables donít have to be primitives to be useful. Final variables that are scoped and constructed can be used as a powerful tool to solidify code in methods.

Method-Scoped final Variables

Although final variables that appear within methods are a little strange to some people at first, they become quite addictive once you get used to reading them. See Example 2-5.

Example 2-5. Catching mistakes with method-scoped final variables

package oreilly.hcj.finalstory;
public class FinalVariables {
  public static String someMethod(final String environmentKey) {
    
final String key = "env." + environmentKey;
    
System.out.println("Key is: " + key);
    return (System.getProperty(key));
  }
}

In this class, you build a scopedfinalvariable that adds a prefix to the parameterenvironmentKey. In this case, thefinalvariable is final only within the execution scope, which is different at each execution of the method. Each time the method is entered, thefinalis reconstructed. As soon as it is constructed, it cannot be changed during the scope of the method execution. This allows you to fix a variable in a method for the duration of the method. To see how this works, use the test program in Example 2-6.

Example 2-6. Testing final variables

package oreilly.hcj.finalstory;
public class FinalVariables {
 
public final static void main(final String[] args){
    System.out.println("Note how the key variable is changed.");
    someMethod("JAVA_HOME");
    someMethod("ANT_HOME");
  }
}

Running this test program results in the following:

>ant -Dexample=oreilly.hcj.finalstory.FinalVariables run_example
run_example:
     [java] Note how the key variable is changed.
     [java] Key is:
env.JAVA_HOME
     [java] Key is:
env.ANT_HOME

Each time the method is entered, the passed-in environmentKey parameter is appended to the constant prefix and then frozen for the duration of the method call. So why make the variable final? Because once this variable is set in the body of the method, it cannot be changed. Consider what would happen if you made a mistake like the one shown in Example 2-7.

Example 2-7. A coding mistake caught by a final variable

package oreilly.hcj.finalstory;
public class FinalVariables {
 
public static String someBuggedMethod(final String environmentKey){
    final String key = "env." + environmentKey;
    System.out.println("Key is: " + key);
   
key = new String("someValue"); // <= compiler error. 
   
return (System.getProperty(key));
  }
}

When you try to compile this code, it will give the following result:

>ant -Dexample=oreilly/hcj/finalstory/FinalVariables.java compile_example
compile_example:
    [javac] Compiling 1 source file to C:devhcjbin 
    [javac]
C:devhcjsrcoreillyhcjfinalstoryFinalVariables.java:53:cannot
assign a value to final variable key
    [javac]   key = new String("someValue"); // <= compiler error.
    [javac]   ^
    [javac] 1 error


In the example code, I commented out the compiler error; you will have to uncomment it to run this test. I use a similar procedure for all compiler errors throughout the book.


In this example, the mistake was made of trying to reassignkeyto a different value. This type of mistake simply happens; however, since you are a savvy programmer, and you used thefinalkeyword, the compiler tells you that an error was made. This is a great example of trading a logic error for a compiler error.

The technique of fixing variables withfinalis extremely handy for long or complicated methods that have many local variables. When alerted by the compiler, repairing this mistake takes a matter of seconds. If you donít use finalto fix your variables now, you run the risk of spending long hours to find logic bugs, only to discover that someone reset your variable halfway through the method because of a typo.


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