Java
  Home arrow Java arrow Page 3 - The Final (Constants) Story
Dev Articles Forums 
ADO.NET  
Apache  
ASP  
ASP.NET  
C#  
C++  
ColdFusion  
COM/COM+  
Delphi-Kylix  
Design Usability  
Development Cycles  
DHTML  
Embedded Tools  
Flash  
Graphic Design  
HTML  
IIS  
Interviews  
Java  
JavaScript  
MySQL  
Oracle  
Photoshop  
PHP  
Reviews  
Ruby-on-Rails  
SQL  
SQL Server  
Style Sheets  
VB.Net  
Visual Basic  
Web Authoring  
Web Services  
Web Standards  
XML  
Mobile Linux 
App Generation ROI 
IBM® developerWorks 
Sun Developer Network 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
JAVA

The Final (Constants) Story
By: O'Reilly Media
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 5 stars5 stars5 stars5 stars5 stars / 4
    2005-10-20

    Table of Contents:
  • The Final (Constants) Story
  • Excessive Constants
  • Deferred Initialization
  • Final Collections
  • Instance-Scoped Variables
  • Final Methods
  • Conditional Compilation Variable Location

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    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.

    More Java Articles
    More By O'Reilly Media


       · This article is an excerpt from the book "Hardcore Java", published by O'Reilly. We...
     

    Buy this book now. This article is excerpted from chapter two of Hardcore Java, written by Robert Simmons Jr. (O'Reilly, 2004; ISBN: 0596005687). Check it out at your favorite bookstore. Buy this book now.

    JAVA ARTICLES

    - 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 ...
    - Generics and Limitations in Java
    - Getting Started with Java Web Development in...






    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 4 hosted by Hostway
    Stay green...Green IT