Composition syntax

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Bruce Eckel’s Thinking in Java Contents | Prev | Next

Until
now, composition has been used quite frequently. You simply place object
handles inside new classes. For example, suppose you’d like an object
that holds several
String
objects, a couple of primitives and an object of another class. For the
non-primitive objects, just put handles inside your new class, and for the
primitives just define them inside your class: (See page
97
if you have trouble executing this program.)

//: SprinklerSystem.java
// Composition for code reuse
package c06;
 
class WaterSource {
  private String s;
  WaterSource() {
    System.out.println("WaterSource()");
    s = new String("Constructed");
  }
  public String toString() { return s; }
}
 
public class SprinklerSystem {
  private String valve1, valve2, valve3, valve4;
  WaterSource source;
  int i;
  float f;
  void print() {
    System.out.println("valve1 = " + valve1);
    System.out.println("valve2 = " + valve2);
    System.out.println("valve3 = " + valve3);
    System.out.println("valve4 = " + valve4);
    System.out.println("i = " + i);
    System.out.println("f = " + f);
    System.out.println("source = " + source);
  }
  public static void main(String[] args) {
    SprinklerSystem x = new SprinklerSystem();
    x.print();
  }
} ///:~ 

One
of the methods defined in
WaterSource
is
special:
toString( ).
You will learn later that every non-primitive object has a toString( )
method, and it’s called in special situations when the compiler wants a
String
but it’s got one of these objects. So in the expression:

System.out.println("source
= " + source);

At
first glance, you might assume – Java being as safe and careful as it is
– that the compiler would automatically construct objects for each of the
handles in the above code, for example calling the default constructor for
WaterSource
to initialize
source.
The output of the print statement is in fact:

valve1 = null
valve2 = null
valve3 = null
valve4 = null
i = 0
f = 0.0
source = null

It
makes sense that the compiler doesn’t just create a default object for
every handle because that would incur unnecessary overhead in many cases. If
you want the handles initialized, you can do it:

  1. At
    the point the objects are defined. This means that they’ll always be
    initialized before the constructor is called.
  2. In
    the constructor for that class
  3. Right
    before you actually need to use the object. This can reduce overhead, if there
    are situations where the object doesn’t need to be created.
All
three approaches are shown here:

//: Bath.java
// Constructor initialization with composition
 
class Soap {
  private String s;
  Soap() {
    System.out.println("Soap()");
    s = new String("Constructed");
  }
  public String toString() { return s; }
}
 
public class Bath {
  private String
    // Initializing at point of definition:
    s1 = new String("Happy"),
    s2 = "Happy",
    s3, s4;
  Soap castille;
  int i;
  float toy;
  Bath() {
    System.out.println("Inside Bath()");
    s3 = new String("Joy");
    i = 47;
    toy = 3.14f;
    castille = new Soap();
  }
  void print() {
    // Delayed initialization:
    if(s4 == null)
      s4 = new String("Joy");
    System.out.println("s1 = " + s1);
    System.out.println("s2 = " + s2);
    System.out.println("s3 = " + s3);
    System.out.println("s4 = " + s4);
    System.out.println("i = " + i);
    System.out.println("toy = " + toy);
    System.out.println("castille = " + castille);
  }
  public static void main(String[] args) {
    Bath b = new Bath();
    b.print();
  }
} ///:~ 

Note
that in the
Bath
constructor
a statement is executed before any of the initializations take place. When you
don’t initialize at the point of definition, there’s still no
guarantee that you’ll perform any initialization before you send a
message to an object handle – except for the inevitable run-time exception.

Here’s
the output for the program:

Inside Bath()
Soap()
s1 = Happy
s2 = Happy
s3 = Joy
s4 = Joy
i = 47
toy = 3.14
castille = Constructed

When
print( )
is called it fills in
s4
so that all the fields are properly initialized by the time they are used.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read