Home arrow Java arrow Page 5 - 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 - Instance-Scoped Variables
(Page 5 of 7 )

Another type of final class member that can be very useful is instance-scoped final attributes. Consider the code in Example 2-14.

Example 2-14. A creation date property

package oreilly.hcj.finalstory;
public class FinalMembers {
 
/** Holds the creation date-time of the instance. */
  private Date creationDate=
    Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();
 
/**
   * Get the Date-Time when the object was created.
   *
   * @return The creation date of the object.
   */
  public Date getCreationDate() {
    return this.creationDate;
 
}
}

The job of the propertycreationDateis to hold the date and time of the instanceís creation. This property represents a read-only property that is set once; after all, an object can be created only once. However, there is a problem with this property: it leaves a massive potential bug lurking in your code. To illustrate this, lets look at another part of the same class in Example 2-15.

Example 2-15. A modification date property

package oreilly.hcj.finalstory;
public class FinalMembers {
 
/** Holds the modification date-time of the instance. */
 
public Date modificationDate = creationDate;
 
public void setModificationDate(Date modificationDate) {
   
if (modificationDate == null){
      throw new NullPointerException();
    } 
      
this.creationDate = modificationDate; 
  
}
  
public Date getModificationDate() {
    return this.modificationDate;
  }
}

Here, you have a neat and cryptic little logic bug. If you didnít see the bug instantly, that only reinforces my point. The problem is that the writer of the setModificationDate() method is obviously setting the wrong parameter. Due to a simple typo, instead of setting modificationDate, this method setscreationDate. No one ever intends to write bugs like this, but it happens. Fortunately, there is a way you can block this problem with a coding standard:

package oreilly.hcj.finalstory;
public class FinalMembers {
 
private final Date creationDate2 =
    Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();
 
public Date getCreationDate2(){
    return this.creationDate2;
  }
 
public void setModificationDate2(Date modificationDate) {
    if (modificationDate == null) {
       throw new NullPointerException();
    }
   
this.creationDate2 = modificationDate; // <= Compiler error.
  }
}

Now that thecreationDate2isfinal, any attempts to write to it after it is initialized will cause a compiler error.

Instance-scopedfinalvariables have a similar morphology to method-scopedfinalvariables. They are constructed once at each instantiation of the class and then are frozen for the life of the instance. They are ideal for read-only attributes set only at instantiation.

This technique gives you yet another coding standard that can save your skin. What used to be a difficult-to-find logic bug is magically transformed by the keywordfinalinto a minor compile-time bug. Finding the logic bug could potentially take hours of painstaking work with a debugger. The compile-time bug you replaced it with would be fixed by a programmer in only a few seconds. Itís unlikely the programmer would even notice the repair!

You can take this concept even further. When writing classes, you can create instance-scoped attributes that arefinaland are not initialized. The class in Example 2-16 shows how this deferred initialization works.

Example 2-16. Deferring final initialization to the constructor

package oreilly.hcj.finalstory;
public class FinalMembers {
 
/** Holds the creation date-time of the instance. */
 
private final Date creationDate3;
 
/**
   * Constructor
   *
   * @param creationDate The creation date.
  
* @param modificationDate The last modification date.
   */
  public FinalMembers(final Date creationDate,
                     final Date modificationDate) {
    if (modificationDate.compareTo(creationDate) < 0) {
      throw new IllegalArgumentException("modificationDate");
    }
   
this.creationDate3 = creationDate;
   
// do a bunch of date calculations.
    this.creationDate3 = modificationDate; // <= compiler error 
  }
 
/**
  
* Second constructor. Use current date for creation date.
   *
  
* @param modificationDate The last modification date.
   */
  public FinalMembers(final Date modificationDate) {
    this.modificationDate = modificationDate;
   
// <= compiler error: 'creationDate may not have been initialized'
 
}
}

In this example, you create a member variable namedcreationDate3and set it asfinal, but donít initialize it. Upon construction, the user passes a date to the class andcreationDate3is set. As before, once it is set, it cannot be changed. This ability to defer an initialization to a constructor gives you a lot of flexibility and safety in object initialization.

In the first constructor, you accidentally try to setcreationDate3twice: once with thecreationDateparameter and once with themodificationDateparameter. Obviously, the author of this file mistyped the variable name and meant to type this.modificationDate in the second instance. However, since you are being vigilant with the finalkeyword, you catch the error at compile time and correct it.

As a bonus, if you forget to set the variable, as in the second constructor in Example 2-16, you get a compiler error telling you that the variable hasnít been initialized:

>ant - Dexample=oreilly/hcj/finalstory/FinalMembers.java compile_example
compile_example:
   
[javac] Compiling 1 source file to C:devhcjbin
    [javac] C:devhcjsrcoreillyhcjfinalstory FinalMembers.java:69: variable creationDate3 might not have been initialized
   
[javac]  public FinalMembers(final Date modificationDate) {
   
[javac]                      ^
   
[javac] 1 error

The reason you get this compiler error is because Java requires that all instance-scopedfinalvariables and members must be set before the end of the constructor. In a similar manner, all class-scoped members must be set by the end of the static initialization of the class they are declared in, and all method-scopedfinals must be set by the end of the method in which they are declared in.

In the case of Example 2-16, your coding standard has given you two layers of security for the price of one. This is a bargain that no programmer could pass up!

Final Classes

A final class is a class that does not allow itself to be inherited by another class. Final classes mark endpoints in the inheritance tree.

There are two ways to make a class final. The first is to use the keywordfinalin the class declaration:

public final class SomeClass {
 
// ...Class contents
}

The second way to make a class final is to declare all of its constructors asprivate:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
 
privateSomeClass(final int value) {
  }
}

When you give all constructorsprivatevisibility, you are implicitly declaring the class as final; often, this is not the intended result. In fact, it is the omission of the keywordfinalon the class declaration that should alert you to the fact that something is wrong. The class above may very well need to be final, in which case you should always specifically use the keywordfinalin the class declaration. If you donít follow this rule, you could end up causing some devious problems.

To find an example of these problems, you need to look no further than the JDK itself. In thejava.beanspackage, you will find a class calledIntrospector(see Chapter 8). Take a look at its single constructor in Example 2-17.*

Example 2-17. The java.beans.Introspector source snippets

public class Introspector {
  // ...snip...
  private Introspector(Class beanClass, Class stopClass, int flags)
 
throws IntrospectionException {
  // ...snip...
  }
}

The constructor for theIntrospectorclass is private. I noticed this while studying this class. My goal was to extend theIntrospectorand create a class that is more feature-rich thanIntrospectoritself. Unfortunately, since the only constructor of the class is private, it is impossible to extend this class. In the case of theIntrospectorclass, there is no reason that the class should be final. The Introspectorclass is a good example of how implicitfinalclasses can cause problems.

Most singleton classes shouldnít be declared asfinal. You never know what other features your classís user will dream up. However, since singleton classes need to be protected from external instantiation, you canít make the constructor public. The solution to the problem is a protected constructor, as shown in Example 2-18.

Example 2-18. An extensible singleton

package oreilly.hcj.finalstory;
public class Singleton {
  private static final Logger LOGGER = Logger.getLogger(Singleton.class);
  public static Singleton instance = null;
  private final String[] params;
 
public static void init(final String[] params){
    // ...do some initialization...
    instance = new Singleton(params);
 
}
  protected Singleton(final String[] params) {
    this.params = params;
    if (LOGGER.isDebugEnabled()) {
     
LOGGER.debug(Arrays.asList(this.params).toString());
    }
  }
}

* J2SDK source code. © 2002 by Sun Microsystems.

Since this singleton class has a protected constructor instead of a private constructor, you can extend its functionality while protecting it against construction by the user. This will allow you to extend the singleton, as shown in Example 2-19.

Example 2-19. An extension of a singleton

package oreilly.hcj.finalstory;
public class ExtendedSingleton extends Singleton {
  private final static int DEFAULT_VALUE = 5;
  private final int value;
 
public static void init(final String[] params){
    instance = new ExtendedSingleton(params, DEFAULT_VALUE);
  }
 
public static void init(final String[] params, final int value) {
    instance = new ExtendedSingleton(params, value);
  }
 
protected ExtendedSingleton(final String[] params, final int value) {
    super(params);
    this.value = value;
 
}
}

The protected constructor technique is not limited to classes with instance variables and methods. A protected constructor should be declared for classes that are entirely static in nature. Although these classes have no other instance members, it would be possible, if a bit pointless, to instantiate them. Preventing this instantiation while providing extensibility is definitely an asset to the development process.

There are rare circumstances when a class should be made final. One example is the concept of the constant object class, which we will discuss in Chapter 5. However, making classes implicitly final is never a good thing; if you want to make a class final, come right out a declare it with the class declaration.


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