Categories
Java patterns

Pattern Patter: Designated Initializer and Static Class

Summary: This is a pair of code patterns for dealing with the dependencies between a subclass and a superclass caused by constructors, and for creating only one copy of a class.

Designated Initializer

Example

It’s easy to have an unexpected constructor be called.

class A {
A() {System.out.print("A(); ");}
A(String s) {System.out.print("A("+s+"); ");}
}

class B extends A {
B() {System.out.print("B(); ");}
B(String s) {System.out.print("B("+s+"); ");}
}

If we call new B() we get “A(); B();”. If we call new B("test"), we get “A(); B(test);”. Suppose we modify A() to call this("default-A"); first. Then new B() yields “A(default-A); A(); B();”.
These are the defined behaviors, part of the Java language specification. However, it can become confusing to keep track of which constructors in the subclass call which ones in the superclass.

Forces

  • Subclass constructors need access to the superclass constructors.
  • The possible connections from subclass to superclass can become too numerous and confusing.

Resolution

In each class, choose one constructor (usually the one with the most parameters), and make it the designated initializer. This constructor will be the only one that goes directly to the superclass constructor (using super(...)). All other constructors in the subclass should use the designated initializer (using this(...).)

Discussion

Without a designated initializer, the connections between the classes may be complicated (and perhaps somewhat arbitrary):

A()-------->A(s)
| |
+----+----+ |
| | |
B() B(x) B(x,y)

Using a designated initializer gives one constructor per class a special status. The other constructors then become peers at a slightly lower level.

A(s)
^ ^------A()
|
B(x,y)
|
B()---^-----B(x)

In this way, we reduce the coupling between the superclass and the subclass.
Note: You need to be somewhat careful about the constructor with no arguments. It is the default superclass constructor (called if none is specified), and it is the constructor that is used when building a class through the Reflection facilities and when creating a Bean. You may find you need a no-arg constructor to support reflection or beans even if you otherwise wouldn’t have one.
Note: This pattern calls for explicit use of super and this to cal other constructors. Their use is permitted but not required in Java.

Other Uses

This pattern is standard in the NeXTSTEP class libraries (using Objective-C). It is applicable to C++ as well, where multiple inheritance can lead to even more interconnections between a class and its parents.

Static Class

Introduction

You have a class that you just don’t want changed. You don’t want users to subclass it. You don’t want them to create instances. This class is really for some static information and methods that don’t need extra instances. This may be to create a particular Singleton, or because of reasons of efficiency or security.

Forces

  • Your class should not be subclassed.
  • Your class should not have instances created by the user.
  • Using Java.

Resolution

Create the class as both final and with a private constructor.

Discussion

The final keyword prevents the class from being subclassed. Declaring the constructor private ensures that nothing outside the class can create an instance. (The constructor must be explicitly declared – by default the Java compiler will declare a constructor public Xxx () {} unless you take steps.)

public final class Xxx {
private Xxx () {}

// Static variables and methods may be defined here
}

You may still have “instance” methods, but the instances would have to be created by this class and served out in sort of a “Factory” style.

Uses

Java uses this for the java.lang.Math class. It also may be of use in a gang-of-four style Singleton.

Written 11-30-97 (Designated Initializer) and 12-12-97 (Non-instantiable class).