Home arrow Java arrow Page 3 - 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 - Deferred Initialization
(Page 3 of 7 )

If you want to, you can defer the initialization of a final variable within the method. Hold onto your hat because Example 2-8 is going to look a little weird at first.

Example 2-8. Method-scoped final variables with deferred initialization

package oreilly.hcj.finalstory;
public class FinalVariables {
 
public void buildGUIDialog (final String name) {
    final String instanceName;
    
if (name == null) {
     
// no problem here.
     
instanceName = getClass().getName() + hashCode();
    } else {
     
// no problem here as well. 
    instanceName = getClass().getName() + name;
  }
 
JDialog dialog = new JDialog();
   
// .. Do a bunch of layout and component building.
   
dialog.setTitle(instanceName);
   
// .. Do dialog assembly
   
instanceName = "hello"; // <= compiler error
  }
}

In this case, you declare a final variable at the start of the method without giving it a value, since the contents of that variable depend on whether the user passed you null. During theifstatement, check fornulland then assign the variable appropriately. Once you assign the variable a value, it canít be assigned a value again. However, you could have gone through half the method before assigning the variable its value. This coding technique allows you to make single-shot, assign-and-freeze variables. After assignment, these variables behave like constants for the rest of the method.


Method-scopedfinalvariables arenít the same as constants, although they behave like constants at times. The difference is that method-scopedfinalvariables are variable. Each time the method is entered, their values are changed based on the needs of that particular execution. However, method-scoped constants always have the same values regardless of the circumstances under which the method is run. Also, primitive andStringmethod-scopedfinalvariables are not substituted at compile time like primitive andStringmethod-scoped constants.


In addition to deferring the initialization of method variables, you can defer the initialization of instance-scoped variables and class-scoped variables. Instance-scoped variables must be initialized in a constructor, and class-scoped variables must be initialized in thestatic{}method or you will receive compiler errors stating that the variable has not been initialized.

Chained deferred initialization

One interesting trick you can employ with deferred initialization is to chain the initialization of multiple final variables together. For example, consider the following code, which chains instance-scoped final variable initialization:

package oreilly.hcj.finalstory;
public class ChainingFinals {
 
public final String name;
  public final int nameLength = this.name.length;
 
// public final String anotherValue = name;  // <== Won't compile
 
public ChainingFinals(final String name) {
    this.name = name;
  }
}

In this code, the emphasized line will work properly because thefinalvariablenamemust be initialized in the constructor to the class. Therefore, thefinalvariablenameLengthcan take advantage ofnamewhen the instance is initialized. However, make sure that you use thethiskeyword in front of the variable name. If you donít, it wonít compile.

package oreilly.hcj.finalstory;
public class ChainingFinals {
 
public final String name;
 
public final int nameLength = name.length; // <== Won't compile
  public ChainingFinals(final String name) {
    this.name = name;
  }
}

In this slightly revised example, I left off the keyword this when using name. As a result, the line wonít compile but will instead tell me that nameis not declared. The compiler requires that you use thethisreference when chainingfinals in this manner.

Chainingfinalinitialization can be a great tool for precaching data or initializingfinalmembers that are dependent on otherfinalmembers. It also has the benefit of giving you insight into how object instantiation is managed in the virtual machine. As a result of this process, the constructor to the class is run before the initializers.

Final Parameters

Just when you think itís safe to hit Compile, you can go even further with finals. To illustrate, suppose you hire a new developer and, while adding a new feature, he decides to make a little change to the equation2() method from Example 2-4. The changes he makes are shown in Example 2-9.

Example 2-9. Danger of nonfinal parameters

package oreilly.hcj.finalstory;
public class FinalParameters {
 
public double equation2(double inputValue) {
    final double K = 1.414;
    final double X = 45.0;
   
double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
   
double powInputValue = 0;
   
if (result > 360) {
      powInputValue = X
* Math.sin(result);
   } else {
      inputValue = K * Math.si(result);
   }
 
    
result = Math.pow(result, powInputValue);
    if (result > 360) {
      result = result / inputValue;
    }
    return result;
  }
}

The problem is that the new guy changed the value of the parameter passed in to the method. During the firstifstatement, the developer made one little mistakeóhe typedinputValueinstead ofpowInputValue. This caused errors in the subsequent calculations in the method. The user of the function expects certain output and doesnít get it; however, the compiler says that everything in the code is okay. Now itís time to put on another pot of coffee and hope your spouse remembers who you are after you figure out this rather annoying problem.

Little bugs like this are often the most difficult to locate. By Murphyís Law, you can absolutely guarantee that this code will be in the middle of a huge piece of your project, and the error reports wonít directly lead you here. Whatís more, you probably wonít notice the impact of the bug until it goes into production and users are screaming for a fix.

You cannot afford to forget that once you write code, the story is not over. People will make changes, additions, and errors in your code. You will have to look through the code and fix everything that was messed up. To prevent this problem from occurring, do the following:

package oreilly.hcj.finalstory;
public class FinalParameters {
public double equation2Better(final double inputValue){
  final double K = 1.414;
  final double X = 45.0;
 
double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
 
double powInputValue = 0;
  if (result > 360) {
      powInputValue = X * Math.sin(result);
  } else {
     
inputValue = K * Math.sin(result);  // <= Compiler error 
  
}
 
result = Math.pow(result, powInputValue);
  if (result > 360) {
    result = result / inputValue;
  }
  
return result;
}

When you state that the parameterinputValueisfinal, the compiler will catch any attempts to assign another value to that parameter and give you an error message with the line number and reason for the problem. The benefit of this little trick (which takes about two seconds to implement) becomes even more obvious when you consider the hypothetical Java bean shown in Example 2-10.

Example 2-10. A bean with a bug

public class Person {
  private String name = null;
  public void setName(String name) throws PropertyVetoException {
   
String oldName = this.name;
    vetoableChangeSupport.fireVetoableChange("name", oldName, name);
   
name = name;
    propertyChangeSupport.firePropertyChange("name", oldName, name);
  }
}

On the emphasized line, the programmer forgot to use the prefixthison the left-hand side of the assignment; the line should have read:

this.name = name;

Instead, the assignment does absolutely nothing. In a data model with 212 objects and over 1,000 attributes, bugs like this are extremely difficult to detect. However, if you have a policy to always label method parameters asfinal, such an assignment will cause a compiler error. Thefinalparameter version is shown here:

public class Person {
  private String name = null;
 
public void setName(final String name) throws PropertyVetoException {
    String oldName = this.name;
    vetoableChangeSupport.fireVetoableChange("name", oldName, name);
   
name = name; // <= Compiler error
    propertyChangeSupport.firePropertyChange("name", oldName, name);
  }
}

When compiling this code, the programmer immediately gets a compiler error on the (erroneous) assignment line. The programmer looks back at the code, spots the bug in about two seconds, fixes it, gets another soda, and continues work without even thinking about how much trouble he just avoided. The coding standard here saved him hours of work.


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