3 Declarations

3.1 Class Declarations

A class declaration introduces a new reference type. It has the following general syntax:

<class modifiers> class <class name><formal type parameter list>
                            <extends clause> <implements clause>   // Class header
{ // Class body
        <field declarations>
        <method declarations>
        <nested class declarations>
        <nested interface declarations>
        <nested enum declarations>
        <constructor declarations>
        <initializer blocks>
}

In the class header, the name of the class is preceded by the keyword class. In addition, the class header can specify the following information:

accessibility modifier (see Section 4.7, p. 132)

• additional class modifiers (see Section 4.8, p. 135)

• a formal type parameter list, if the class is generic (see Section 14.2, p. 663)

• any class it extends (see Section 7.1, p. 284)

• any interfaces it implements (see Section 7.6, p. 309)

The class body can contain member declarations which comprise:

field declarations (see Section 2.3, p. 31)

method declarations (see Section 3.3, p. 44)

nested class, enum, and interface declarations (see Section 8.1, p. 352)

Members declared static belong to the class and are called static members. Nonstatic members belong to the objects of the class and are called instance members. In addition, the following can be declared in a class body:

constructor declarations (see Section 3.4, p. 48)

static and instance initializer blocks (see Section 9.7, p. 406)

The member declarations, constructor declarations, and initializer blocks can appear in any order in the class body.

In order to understand what code can be legally declared in a class, we distinguish between static context and non-static context. A static context is defined by static methods, static field initializers, and static initializer blocks. A non-static context is defined by instance methods, constructors, non-static field initializers, and instance initializer blocks. By static code we mean expressions and statements in a static context, and similarly by non-static code we mean expressions and statements in a non-static context. One crucial difference between the two contexts is that static code can only refer to other static members.

3.2 JavaBeans Standard

The JavaBeans Standard allows reusable software components to be modelled in Java so that these components can be assembled to create sophisticated applications. In particular, builder tools can take advantage of how these components are specified, in order to build new applications based on these components. The JavaBeans specification specifies the rules for defining such components (called JavaBeans). The interested reader is encouraged to consult this documentation (see http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html) for details since we only cover the basic fundamentals for creating JavaBeans .

Naming Patterns for Properties

The rules of the JavaBean specification stipulate naming patterns for declaring properties of JavaBeans. A naming pattern defines a standard naming convention. A property of an object is normally defined as a field in the object, which is usually not directly accessible by clients (see Example 3.1). A JavaBean should adhere to the following naming patterns when specifying its properties:

• The properties are assumed to be private, and their names start with a lowercase letter. Example 3.1 shows that the JavaBean class Light has three properties.

• In order to retrieve and change values of its properties, a JavaBean provides getter and setter methods for them. Example 3.1 shows a JavaBean with three getter and three setter methods for its properties.

• For a property, the setter method starts with the prefix set. The rest of the method name is assumed to be a property name, where the first letter of the property name has been converted to uppercase. In Example 3.1, the value of the property noOfWatts can be changed by the setter method setNoOfWatts().

Setter methods are public and void, having a parameter of the same type as that of the property.

• For a property, the getter method starts with the prefix get. The rest of the method name is assumed to be a property name, where the first letter of the property name has been converted to uppercase. In Example 3.1, the value of the property noOfWatts can be retrieved by the getter method getNoOfWatts().

For a boolean property, the getter method can start with the prefix get or is. In Example 3.1, the value of the boolean property indicator can be retrieved by the getter method isIndicator().

Getter methods are no-argument public methods that return a value of the same type as the parameter of the corresponding setter method.

Example 3.1 A JavaBean

public class Light {
  // Properties:
  private int     noOfWatts;       // wattage
  private String  location;        // placement
  private boolean indicator;       // on or off

  // Setters
  public void setNoOfWatts(int noOfWatts)     { this.noOfWatts = noOfWatts; }
  public void setLocation(String location)    { this.location = location; }
  public void setIndicator(boolean indicator) { this.indicator = indicator; }

  // Getters
  public int     getNoOfWatts() { return noOfWatts; }
  public String  getLocation()  { return location; }
  public boolean isIndicator()  { return indicator; }
}

Naming Patterns for the Event Model

A listener is an object that is interested in being notified when a particular event takes place. The origin of this event is usually an object called the source, which notifies interested listeners when the event occurs. In this setup, a listener can be added to or removed from the list of listeners notified by a source about the occurrence of a particular event. This setup is the basis of the event model which is depicted in Figure 3.1.

Figure 3.1 The Event Model

The Event Model

The JavaBean specification stipulates naming patterns for the event model to facilitate its use by builder tools to assemble event-based applications. Figure 3.1 shows where the naming patterns for handling events of type X are applied:

• An event class with the name XEvent, that extends the java.util.EventObject class.

public class XEvent extends java.util.EventObject {
  public XEvent(Object source) {
    super(source);
  }
}

• A listener interface with the name XListener, that specifies the specific method to be called in a listener when an event of the type XEvent occurs. The listener interface extends the java.util.EventListener interface.

public interface XListener extends java.util.EventListener {
  public void methodAInXListener(XEvent ev);
}

A listener interested in XEvents must implement the XListener interface, and must be registered with the source in order to be informed about XEvents.

public class ListenerObject implements XListener {
  public void methodAInXListener(XEvent e) { /* ... */ }
}

• A source for XEvent, that implements the methods addXListener() and remove-XListener(). These methods are used to add or remove a listener interested in XEvents, respectively. The parameter of these methods is of the type XListener.

public class SourceObject {
  public synchronized void addXListener(XListener listener) { /* ... */ }
  public synchronized void removeXListener(XListener listener) { /* ... */ }
}

Note that there are no naming patterns defined for the names of the source and the listener classes. Neither is there any standard convention for naming the methods specified in the listener interface.

3.3 Method Declarations

The general syntax of a method declaration is

<method modifiers> <formal type parameter list> <return type> <method name>
              (<formal parameter list>) <throws clause> // Method header

{ // Method body
         <local variable declarations>
         <nested local class declarations>
         <statements>
}

In addition to the name of the method, the method header can specify the following information:

• scope or accessibility modifier (see Section 4.9, p. 138)

• additional method modifiers (see Section 4.10, p. 146)

• a formal type parameter list, if the declaration is for a generic method (see Section 14.8, p. 697)

• the type of the return value, or void if the method does not return any value (see Section 6.4, p. 228)

• a formal parameter list (see below)

checked exceptions thrown by the method are specified in a throws clause (see Section 6.9, p. 257)

The formal parameter list is a comma-separated list of parameters for passing information to the method when the method is invoked by a method call (see Section 3.7, p. 81). An empty parameter list must be specified by ( ). Each parameter is a simple variable declaration consisting of its type and name:

<parameter modifier> <type> <parameter name>

The parameter names are local to the method (see Section 4.6, p. 131). The parameter modifier final is discussed in Section 3.7 on page 89.

The signature of a method comprises the method name and the formal parameter list only.

The method body is a block containing the local declarations and the statements of the method. Local variable declarations are discussed in Section 2.3 on page 31, and nested local class declarations in Section 8.4 on page 371.

Like member variables, member methods can be characterized as:

instance methods

static methods, which are discussed in Section 4.10, p. 148.

Statements

Statements in Java can be grouped into various categories. Variable declarations with explicit initialization of the variables are called declaration statements (see Section 2.3, p. 31, and Section 3.6, p. 71). Other basic forms of statements are control flow statements (see Section 6.1, p. 204) and expression statements.

An expression statement is an expression terminated by a semicolon. The expression is evaluated for its side effect and its value discarded. Only certain types of expressions have meaning as statements. They include the following:

• assignments (see Section 5.5, p. 169)

• increment and decrement operators (see Section 5.8, p. 186)

• method calls (see Section 3.7, p. 81)

• object creation expressions with the new operator (see Section 5.15, p. 201)

A solitary semicolon denotes the empty statement that does nothing.

A block, {}, is a compound statement which can be used to group zero or more local declarations and statements (see Section 4.6, p. 131). Blocks can be nested, since a block is a statement that can contain other statements. A block can be used in any context where a simple statement is permitted. The compound statement which is embodied in a block, begins at the left brace, {, and ends with a matching right brace, }. Such a block must not be confused with an array initialization block in declaration statements (see Section 3.6, p. 71).

Labeled statements are discussed in Section 6.4 on page 223.

Instance Methods and the Object Reference this

Instance methods belong to every object of the class and can only be invoked on objects. All members defined in the class, both static and non-static, are accessible in the context of an instance method. The reason is that all instance methods are passed an implicit reference to the current object, that is, the object on which the method is being invoked. The current object can be referenced in the body of the instance method by the keyword this. In the body of the method, the this reference can be used like any other object reference to access members of the object. In fact, the keyword this can be used in any non-static context. The this reference can be used as a normal reference to reference the current object, but the reference cannot be modified—it is a final reference (Section 4.10, p. 148).

The this reference to the current object is useful in situations where a local variable hides, or shadows, a field with the same name. In Example 3.2, the two parameters noOfWatts and indicator in the constructor of the Light class have the same names as the fields in the class. The example also declares a local variable location, which has the same name as one of the fields. The reference this can be used to distinguish the fields from the local variables. At (1), the this reference is used to identify the field noOfWatts, which is assigned the value of the parameter noOfWatts. Without the this reference at (2), the value of the parameter indicator is assigned back to this parameter, and not to the field by the same name, resulting in a logical error. Similarly at (3), without the this reference, it is the local variable location that is assigned the value of the parameter site, and not the field by the same name.

Example 3.2 Using the this Reference

public class Light {
  // Fields:
  int     noOfWatts;      // wattage
  boolean indicator;      // on or off
  String  location;       // placement

  // Constructor
  public Light(int noOfWatts, boolean indicator, String site) {
    String location;

    this.noOfWatts = noOfWatts;    // (1) Assignment to field.
    indicator = indicator;         // (2) Assignment to parameter.
    location = site;               // (3) Assignment to local variable.
    this.superfluous();            // (4)
    superfluous();                 // equivalent to call at (4)
  }

  public void superfluous() { System.out.println(this); } // (5)

  public static void main(String[] args) {
    Light light = new Light(100, true, "loft");
    System.out.println("No. of watts: " + light.noOfWatts);
    System.out.println("Indicator: "    + light.indicator);
    System.out.println("Location: "     + light.location);
  }
}

Output from the program:

Light@df6ccd
Light@df6ccd
No. of watts: 100
Indicator: false
Location: null

If a member is not shadowed by a local declaration, the simple name member is considered a short-hand notation for this.member. In particular, the this reference can be used explicitly to invoke other methods in the class. This is illustrated at (4) in Example 3.2, where the method superfluous() is called.

If, for some reason, a method needs to pass the current object to another method, it can do so using the this reference. This is illustrated at (5) in Example 3.2, where the current object is passed to the println() method.

Note that the this reference cannot be used in a static context, as static code is not executed in the context of any object.

Method Overloading

Each method has a signature, which comprises the name of the method, and the types and order of the parameters in the formal parameter list. Several method implementations may have the same name, as long as the method signatures differ. This is called method overloading. Since overloaded methods have the same name, their parameter lists must be different.

Rather than inventing new method names, method overloading can be used when the same logical operation requires multiple implementations. The Java standard library makes heavy use of method overloading. For example, the class java.lang.Math contains an overloaded method min(), which returns the minimum of two numeric values.

public static double min(double a, double b)
public static float min(float a, float b)
public static int min(int a, int b)
public static long min(long a, long b)

In the following examples, five implementations of the method methodA are shown:

void methodA(int a, double b) { /* ... */ }      // (1)
int  methodA(int a)           { return a; }      // (2)
int  methodA()                { return 1; }      // (3)
long methodA(double a, int b) { return b; }      // (4)
long methodA(int x, double y) { return x; }      // (5) Not OK.

The corresponding signatures of the five methods are as follows:

methodA(int, double)                                   1′

methodA(int)                                                   2′: Number of parameters.

methodA()                                                         3′: Number of parameters.

methodA(double, int)                                   4′: Order of parameters.

methodA(int, double)                                   5′: Same as 1′.

The first four implementations of the method named methodA are overloaded correctly, each time with a different parameter list and, therefore, different signatures. The declaration at (5) has the same signature methodA(int, double) as the declaration at (1) and is, therefore, not a valid overloading of this method.

void bake(Cake k)  { /* ... */ }                 // (1)
void bake(Pizza p) { /* ... */ }                 // (2)

int     halfIt(int a) { return a/2; }            // (3)
double  halfIt(int a) { return a/2.0; }          // (4) Not OK. Same signature.

The method named bake is correctly overloaded at (1) and (2), with two different signatures. In the implementation, changing just the return type (as shown at (3) and (4) above), is not enough to overload a method, and will be flagged as a compile-time error. The parameter list in the declarations must be different.

Only methods declared in the same class and those that are inherited by the class can be overloaded. Overloaded methods should be considered as individual methods that just happen to have the same name. Methods with the same name are allowed, since methods are identified by their signature. At compile time, the right implementation of an overloaded method is chosen based on the signature of the method call. Details of method overloading resolution can be found in Section 7.10 on page 324. Method overloading should not be confused with method overriding (see Section 7.2, p. 288).

3.4 Constructors

The main purpose of constructors is to set the initial state of an object, when the object is created by using the new operator.

A constructor has the following general syntax:

<accessibility modifier> <class name> (<formal parameter list>)
                                    <throws clause> // Constructor header

   { // Constructor body
           <local variable declarations>
           <nested local class declarations>
           <statements>
}

Constructor declarations are very much like method declarations. However, the following restrictions on constructors should be noted:

• Modifiers other than an accessibility modifier are not permitted in the constructor header. For accessibility modifiers for constructors, see Section 4.9 on page 138.

• Constructors cannot return a value and, therefore, do not specify a return type, not even void, in the constructor header. But their declaration can use the return statement that does not return a value in the constructor body (Section 6.4, p. 228).

• The constructor name must be the same as the class name.

Class names and method names exist in different namespaces. Thus, there are no name conflicts in Example 3.3, where a method declared at (2) has the same name as the constructor declared at (1). However, using such naming schemes is strongly discouraged.

Example 3.3 Namespaces

public class Name {

  Name() {                      // (1)
    System.out.println("Constructor");
  }

  void Name() {                 // (2)
    System.out.println("Method");
  }

  public static void main(String[] args) {
    new Name().Name();          // (3) Constructor call followed by method call.
  }
}

Output from the program:

Constructor
Method

The Default Constructor

A default constructor is a constructor without any parameters, i.e., it is a no-parameter constructor. It has the following signature:

<class name>()

If a class does not specify any constructors, then an implicit default constructor is generated for the class by the compiler. The implicit default constructor is equivalent to the following implementation:

<class name>() { super(); }   // No parameters. Calls superclass constructor.

The only action taken by the implicit default constructor is to call the superclass constructor. This ensures that the inherited state of the object is initialized properly (see Section 7.5, p. 302). In addition, all instance variables in the object are set to the default value of their type, barring those that are initialized by an initialization expression in their declaration.

In the following code, the class Light does not specify any constructors.

class Light {
  // Fields:
  int     noOfWatts;       // wattage
  boolean indicator;       // on or off
  String  location;        // placement

  // No constructors
  //...
}

class Greenhouse {
  // ...
  Light oneLight = new Light();     // (1) Call to implicit default constructor.
}

In the code above, the following implicit default constructor is called when a Light object is created by the object creation expression at (1):

Light() { super(); }

Creating an object using the new operator with the implicit default constructor, as at (1), will initialize the fields of the object to their default values (that is, the fields noOfWatts, indicator, and location in a Light object will be initialized to 0, false, and null, respectively).

A class can choose to provide an implementation of the default constructor. In the following example, the class Light provides an explicit default constructor at (1). Note that it has the same name as the class, and that it does not specify any parameters.

class Light {
  // ...
  // Explicit Default Constructor:
  Light() {                        // (1)
    noOfWatts = 50;
    indicator = true;
    location  = "X";
  }
  //...
}

class Greenhouse {
  // ...
  Light extraLight = new Light();   // (2) Call of explicit default constructor.
}

The explicit default constructor ensures that any object created with the object creation expression new Light(), as at (2), will have its fields noOfWatts, indicator and location initialized to 50, true and "X", respectively.

If a class defines any explicit constructors, it can no longer rely on the implicit default constructor to set the state of its objects. If such a class requires a default constructor, its implementation must be provided. In the example below, the class Light only provides a non-default constructor at (1). It is called at (2) when an object of the class Light is created with the new operator. Any attempt to call the default constructor will be flagged as a compile-time error, as shown at (3).

class Light {
  // ...
  // Only non-default Constructor:
  Light(int noOfWatts, boolean indicator, String location) {          // (1)
    this.noOfWatts = noOfWatts;
    this.indicator = indicator;
    this.location  = location;
  }

  //...
}

class Greenhouse {
  // ...
  Light moreLight  = new Light(100, true, "Greenhouse");   // (2) OK.
//Light firstLight = new Light();                          // (3) Compile-time
error.
}

Overloaded Constructors

Like methods, constructors can also be overloaded. Since the constructors in a class all have the same name as the class, their signatures are differentiated by their parameter lists. In the following example, the class Light now provides both an explicit implementation of the default constructor at (1) and a non-default constructor at (2). The constructors are overloaded, as is evident by their signatures. The non-default constructor is called when an object of the class Light is created at (3), and the default constructor is likewise called at (4). Overloading of constructors allows appropriate initialization of objects on creation, depending on the constructor invoked (see also chaining of constructors in Section 7.5, p. 302.)

class Light {
  // ...
  // Explicit Default Constructor:
  Light() {                                                  // (1)
    noOfWatts = 50;
    indicator = true;
    location  = "X";
  }

  // Non-default Constructor:
  Light(int noOfWatts, boolean indicator, String location) { // (2)
    this.noOfWatts = noOfWatts;
    this.indicator = indicator;
    this.location  = location;
  }
  //...
}

class Greenhouse {
  // ...
  Light moreLight  = new Light(100, true, "Greenhouse");     // (3) OK.
  Light firstLight = new Light();                            // (4) OK.
}

Review Questions

Review Questions

3.1 Which one of these declarations is a valid method declaration?

Select the one correct answer.

(a) void method1         { /* ... */ }

(b) void method2()       { /* ... */ }

(c) void method3(void)   { /* ... */ }

(d) method4()            { /* ... */ }

(e) method5(void)        { /* ... */ }

3.2 Which statements, when inserted at (1), will not result in compile-time errors?

public class ThisUsage {
  int planets;
  static int suns;

  public void gaze() {
    int i;
    // (1) INSERT STATEMENT HERE
  }
}

Select the three correct answers.

(a) i = this.planets;

(b) i = this.suns;

(c) this = new ThisUsage();

(d) this.i = 4;

(e) this.suns = planets;

3.3 Given the following pairs of method declarations, which statements are true?

void fly(int distance) {}
int  fly(int time, int speed) { return time*speed; }

void fall(int time) {}
int  fall(int distance) { return distance; }

void glide(int time) {}
void Glide(int time) {}

Select the two correct answers.

(a) The first pair of methods will compile, and overload the method name fly.

(b) The second pair of methods will compile, and overload the method name fall.

(c) The third pair of methods will compile, and overload the method name glide.

(d) The second pair of methods will not compile.

(e) The third pair of methods will not compile.

3.4 Given a class named Book, which one of these constructor declarations is valid for the class Book?

Select the one correct answer.

(a) Book(Book b) {}

(b) Book Book() {}

(c) private final Book() {}

(d) void Book() {}

(e) public static void Book(String[] args) {}

(f) abstract Book() {}

3.5 Which statements are true?

Select the two correct answers.

(a) A class must define a constructor.

(b) A constructor can be declared private.

(c) A constructor can return a value.

(d) A constructor must initialize all fields when a class is instantiated.

(e) A constructor can access the non-static members of a class.

3.6 What will be the result of compiling the following program?

public class MyClass {
  long var;

  public void MyClass(long param) { var = param; }  // (1)

  public static void main(String[] args) {
    MyClass a, b;
    a = new MyClass();                            // (2)
    b = new MyClass(5);                           // (3)
  }
}

Select the one correct answer.

(a) A compilation error will occur at (1), since constructors cannot specify a return value.

(b) A compilation error will occur at (2), since the class does not have a default constructor.

(c) A compilation error will occur at (3), since the class does not have a constructor that takes one argument of type int.

(d) The program will compile without errors.

3.5 Enumerated Types

An enumerated type defines a finite set of symbolic names and their values. These symbolic names are usually called enum constants or named constants. One way to define such constants is to declare them as final, static variables in a class (or interface) declaration:

public class MachineState {
  public static final int BUSY = 1;
  public static final int IDLE = 0;
  public static final int BLOCKED = -1;
}

Such constants are not typesafe, as any int value can be used where we need to use a constant declared in the MachineState class. Such a constant must be qualified by the class (or interface) name, unless the class is extended (or the interface is implemented). When such a constant is printed, only its value (for example, 0), and not its name (for example, IDLE) is printed. A constant also needs recompiling if its value is changed, as the values of such constants are compiled into the client code.

An enumerated type in Java is much more powerful than the approach outlined above. It is certainly more convenient to use than implementing one from scratch using the typesafe enum pattern (see Effective Java by Josh Bloch, ISBN-10: 0321356683).

Declaring Typesafe Enums

The canonical form of declaring an enum type is shown below.

enum MachineState { BUSY, IDLE, BLOCKED } // Canonical form

The keyword enum is used to declare an enum type. The basic notation requires the type name and a comma-separated list of enum constants. In this case, the name of the enum type is MachineState. It defines three enum constants. An enum constant can be any legal Java identifier, but the convention is to use uppercase letters in the name. Essentially, an enum declaration defines a reference type that has a finite number of permissible values referenced by the enum constants, and the compiler ensures they are used in a typesafe manner.

Using Typesafe Enums

Example 3.4 illustrates using enum constants. An enum type is essentially used as any other reference type, and the restrictions are noted later in this section. Enum constants are in fact final, static variables of the enum type, and they are implicitly initialized with objects of the enum type when the enum type is loaded at runtime. Since the enum constants are static members, they can be accessed using the name of the enum type—analogous to accessing static members in a class.

Example 3.4 shows a machine client that uses a machine whose state is an enum constant. From Example 3.4, we see that an enum constant can be passed as an argument, as shown as (1), and we can declare references whose type is an enum type, as shown as (3), but we cannot create new constants (that is, objects) of the enum type MachineState. An attempt to do so at (5), results in a compile-time error.

The string representation of an enum constant is its name, as shown at (4). Note that it is not possible to pass a type of value other than a MachineState enum constant in the call to the method setState() of the Machine class, as shown at (2).

Example 3.4 Using Enums

// Filename: MachineState.java
public enum MachineState { BUSY, IDLE, BLOCKED }

// Filename: Machine.java
public class Machine {

  private MachineState state;

  public void setState(MachineState state) { this.state = state; }
  public MachineState getState() { return this.state; }
}

// Filename: MachineClient.java
public class MachineClient {
  public static void main(String[] args) {

    Machine machine = new Machine();
    machine.setState(MachineState.IDLE);          // (1) Passed as a value.
    // machine.setState(1);                       // (2) Compile-time error!

    MachineState state = machine.getState();      // (3) Declaring a reference.
    System.out.println(
        "The machine state is: " + state          // (4) Printing the enum name.
    );
    // MachineState newState = new MachineState();// (5) Compile-time error!
  }
}

Output from the program:

The machine state is: IDLE

Declaring Enum Constructors and Members

An enum type declaration is a special kind of reference type declaration. It can declare constructors and other members as in an ordinary class, but the enum constants must be declared before any other declarations (see the declaration of the enum type Meal in Example 3.5). The list of enum constants must be terminated by a semi-colon (;). Each enum constant name can be followed by an argument list that is passed to the constructor of the enum type having the matching parameter signature.

In Example 3.5, the enum type Meal contains a constructor declaration at (1) with the following signature:

Meal(int, int)

Each enum constant is specified with an argument list with the signature (int, int) that matches the constructor signature. In addition, the enum declaration declares two fields for the meal time at (3), and two instance methods to retrieve the meal time at (4).

When the enum type is loaded at runtime, the constructor is run for each enum constant, passing the argument values specified for the enum constant. For the Meal enum type, three objects are created that are initialized with the specified argument values, and are referenced by the three enum constants, respectively. Note that each enum constant is a final, static reference that stores the reference value of an object of the enum type, and methods of the enum type can be called on this object by using the enum constant name. This is illustrated at (5) in Example 3.5 by calling methods on the object referenced by the enum constant Meal.BREAKFAST.

An implicit standard constructor is created if no constructors are provided for the enum type. As mentioned earlier, an enum type cannot be instantiated using the new operator. The constructors cannot be called explicitly. The only accessibility modifier allowed for a constructor is private.

Example 3.5 Declaring Enum Constructors and Members

// Filename: Meal.java
public enum Meal {
  BREAKFAST(7,30), LUNCH(12,15), DINNER(19,45);             // (1)

  // Non-default constructor                                   (2)
  Meal(int hh, int mm) {
    assert (hh >= 0 && hh <= 23): "Illegal hour.";
    assert (mm >= 0 && mm <= 59): "Illegal mins.";
    this.hh = hh;
    this.mm = mm;
  }

  // Fields for the meal time:                                 (3)
  private int hh;
  private int mm;

  // Instance methods:                                         (4)
  public int getHour() { return this.hh; }
  public int getMins() { return this.mm; }
}

// Filename: MealAdministrator.java
public class MealAdministrator {
  public static void main(String[] args) {

    System.out.printf(                                      // (5)
        "Please note that no eggs will be served at %s, %02d:%02d.%n",
        Meal.BREAKFAST, Meal.BREAKFAST.getHour(), Meal.BREAKFAST.getMins()
    );

    System.out.println("Meal times are as follows:");
    Meal[] meals = Meal.values();                           // (6)
    for (Meal meal : meals)                                 // (7)
      System.out.printf("%s served at %02d:%02d%n",
                 meal, meal.getHour(), meal.getMins()
      );

    Meal formalDinner = Meal.valueOf("DINNER");             // (8)
    System.out.printf("Formal dress is required for %s at %02d:%02d.%n",
        formalDinner, formalDinner.getHour(), formalDinner.getMins()
    );
  }
}

Output from the program:

Please note that no eggs will be served at BREAKFAST, 07:30.
Meal times are as follows:
BREAKFAST served at 07:30
LUNCH served at 12:15
DINNER served at 19:45
Formal dress is required for DINNER at 19:45.

Implicit Static Methods for Enum Types

All enum types implicitly have the following static methods, and methods with these names cannot be declared in an enum type declaration:

static EnumTypeName[] values()

Returns an array containing the enum constants of this enum type, in the order they are specified.

static EnumTypeName valueOf(String name)

Returns the enum constant with the specified name. An IllegalArgumentException is thrown if the specified name does not match the name of an enum constant. The specified name is not qualified with the enum type name.

The static method values() is called at (6) in Example 3.5 to create an array of enum constants. This array is traversed in the for(:) loop at (7), printing the information about each meal. The for(:) loop is discussed in Section 6.3, p. 220.

The static method valueOf() is called at (8) in Example 3.5 to retrieve the enum constant that has the specified name "DINNER". A printf statement is used to print the information about the meal denoted by this enum constant.

Inherited Methods from the Enum Class

All enum types are subtypes of the java.lang.Enum class which provides the default behavior. All enum types are comparable (Section 15.1, p. 765) and serializable (Section 11.6, p. 510).

All enum types inherit the following final methods from the java.lang.Enum class, and these methods can therefore not be overridden by an enum type:

protected final Object clone()

An instance of an enum type cannot be cloned (see Section 10.2, p. 424). The method throws an CloneNotSupportedException.

final int compareTo(E o)

The natural order of the enum constants in an enum type is according to their ordinal values (see the ordinal() method below). The compareTo() method in the Comparable interface is discussed in Section 15.1, p. 765.

final boolean equals(Object other)

This method returns true if the specified object is equal to this enum constant (Section 15.1, p. 751).

protected final void finalize()

An enum constant cannot be finalized, because this final method effectively prevents enum types from implementing their own finalize() method (see Section 9.4, p. 396).

final Class<E> getDeclaringClass()

This method returns the Class object corresponding to this enum constant’s enum type (see Section 10.2, p. 424).

final int hashCode()

This method returns a hash code for this enum constant (see Section 15.1, p. 760).

final String name()

This method returns the name of this enum constant, exactly as declared in its enum declaration.

final int ordinal()

This method returns the ordinal value of this enum constant (that is, its position in its enum type declaration). The first enum constant is assigned an ordinal value of zero. If the ordinal value of an enum constant is less than the ordinal value of another enum constant of the same enum type, the former occurs before the latter in the enum type declaration.

Note that the equality test implemented by the equals() method is based on reference equality (==) of the enum constants, not on value equality (Section 5.11, p. 193). An enum type has a finite number of distinct objects. Comparing two enum references for equality means determining whether they store the reference value of the same enum contant, i.e., whether the references are aliases. Thus, for any two enum references meal1 and meal2, the expression meal1.equals(meal2) and meal1 == meal2 are equivalent.

The Enum class also overrides the toString() method from the Object class (see Section 10.2, p. 424). The toString() method returns the name of the enum constant, but it is not final, and can be overridden by an enum type. Example 3.6 uses some of the methods mentioned in this subsection.

Extending Enum Types: Constant-Specific Class Bodies

A review of subtyping (Section 7.1, p. 284), overriding (Section 7.2, p. 288), and anonymous classes (Section 8.5, p. 377) can be helpful before diving into this subsection.

Constant-specific class bodies define anonymous classes inside an enum type, i.e., they implicitly extend the enclosing enum type. The enum type Meal in Example 3.6 declares constant-specific class bodies for its constants. The following skeletal code declares the constant-specific class body for the enum constant BREAKFAST:

BREAKFAST(7,30) {                     // (1) Start of constant-specific class body
  public double mealPrice(Day day) {  // (2) Overriding abstract method
    ...
  }
  public String toString() {          // (3) Overriding method from the Enum
class
    ...
  }
}                                     // (4) End of constant-specific class body

The constant-specific class body, as the name implies, is a class body that is specific to a particular enum constant. As any class body, it is enclosed in braces, { }. It is declared immediately after the enum constant and any constructor arguments. In the code above, it starts at (1) and ends at (4). Like any class body, it can contain member declarations. In the above case, the body contains two method declarations: an implementation of the method mealPrice() at (2) that overrides the abstract method declaration at (7) in the enclosing enum supertype, and an implementation of the toString() method at (3) that overrides the one inherited by the Meal enum type from the superclass java.lang.Enum.

The constant-specific class body is an anonymous class, i.e., a class with no name. Each constant-specific class body defines a distinct, albeit anonymous, subtype of the enclosing enum type. In the code above, the constant-specific class body defines a subtype of the Meal enum type. It inherits members of the enclosing enum supertype, that are not private, overridden, or hidden. When the enum type Meal is loaded at runtime, this constant-specific class body is instantiated, and the reference value of the instance is assigned to the enum constant BREAKFAST. Note that the type of the enum constant is Meal, which is the supertype of the anonymous subtype represented by the constant-specific class body. Since supertype references can refer to subtype objects, the above assignment is legal.

Each enum constant overrides the abstract method mealPrice() declared in the enclosing enum supertype, i.e., provides an implementation for the method. The compiler will report an error if this is not the case. Although the enum type declaration specifies an abstract method, the enum type declaration is not declared abstract—contrary to an abstract class. Given that the references meal and day are of the enum types Meal and Day from Example 3.6, respectively, the method call

meal.mealPrice(day)

will execute the mealPrice() method from the constant-specific body of the enum constant denoted by the reference meal.

Two constant-specific class bodies, associated with the enum constants BREAKFAST and LUNCH, override the toString() method from the Enum class. Note that the toString() method is not overridden in the Meal enum type, but in the anonymous classes represented by two constant-specific class bodies. The third enum constant, DINNER, relies on the toString() method inherited from the Enum class.

Constructors, abstract methods, and static methods cannot be declared in a constantspecific class body. Instance methods declared in constant-specific class bodies are only accessible if they override methods in the enclosing enum supertype.

Example 3.6 Declaring Constant-Specific Class Bodies

// Filename: Day.java
public enum Day {
  MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// Filename: Meal.java
public enum Meal {
  // Each enum constant defines a constant-specific class body
  BREAKFAST(7,30) {                                                   // (1)
    public double mealPrice(Day day) {                                // (2)
      double breakfastPrice = 10.50;
      if (day.equals(Day.SATURDAY) || day == Day.SUNDAY)
        breakfastPrice *= 1.5;
      return breakfastPrice;
    }
    public String toString() {                                        // (3)
      return "Breakfast";
    }
  },                                                                  // (4)
  LUNCH(12,15) {
    public double mealPrice(Day day) {                                // (5)
      double lunchPrice = 20.50;
      switch (day) {
        case SATURDAY: case SUNDAY:

         lunchPrice *= 2.0;
      }
      return lunchPrice;
    }
    public String toString() {
      return "Lunch";
    }
  },
  DINNER(19,45) {
    public double mealPrice(Day day) {                                // (6)
      double dinnerPrice = 25.50;
      if (day.compareTo(Day.SATURDAY) >= 0 && day.compareTo(Day.SUNDAY) <= 0)
        dinnerPrice *= 2.5;
      return dinnerPrice;
    }
  };

  // Abstract method implemented in constant-specific class bodies.
  abstract double mealPrice(Day day);                                 // (7)

  // Enum constructor:
  Meal(int hh, int mm) {
    assert (hh >= 0 && hh <= 23): "Illegal hour.";
    assert (mm >= 0 && mm <= 59): "Illegal mins.";
    this.hh = hh;
    this.mm = mm;
  }

  // Instance fields: Time for the meal.
  private int hh;
  private int mm;

  // Instance methods:
  public int getHour() { return this.hh; }
  public int getMins() { return this.mm; }

}

// Filename: MealPrices.java
public class MealPrices {

  public static void main(String[] args) {                               // (8)
    System.out.printf(
        "Please note that %s, %02d:%02d, on %s costs $%.2f.%n",
        Meal.BREAKFAST.name(),                                           // (9)
        Meal.BREAKFAST.getHour(), Meal.BREAKFAST.getMins(),
        Day.MONDAY,
        Meal.BREAKFAST.mealPrice(Day.MONDAY)                             // (10)
    );

    System.out.println("Meal prices on " + Day.SATURDAY + " are as follows:");
    Meal[] meals = Meal.values();
    for (Meal meal : meals)
      System.out.printf(
          "%s costs $%.2f.%n", meal, meal.mealPrice(Day.SATURDAY)        // (11)

      );
  }
}

Output from the program:

Please note that BREAKFAST, 07:30, on MONDAY costs $10.50.
Meal prices on SATURDAY are as follows:
Breakfast costs $15.75.
Lunch costs $41.00.
DINNER costs $63.75.

In Example 3.6, the mealPrice() method declaration at (2) uses both the equals() method and the == operator to compare enum constants for equality. The mealPrice() method declaration at (5) uses enum constants in a switch statement (Section 6.2, p. 207). Note that the case labels in the switch statement are enum constant names, without the enum type name. The mealPrice() method declaration at (6) uses the compareTo() method to compare enum constants.

The main() method at (8) in Example 3.6 demonstrates calling the mealPrice() method in the constant-specific class bodies. The mealPrice() method is called at (10) and (11). Example 3.6 also illustrates the difference between the name() and the toString() methods of the enum types. The name() method is called at (9), and the toString() method is called at (10) and (11). The name() method always prints the enum constant name exactly as it was declared. Which toString() method is executed depends on whether the toString() method in the Enum class is overridden. Only the constant-specific class bodies of the enum constants BREAKFAST and LUNCH override this method. The output from the program confirms this to be the case.

Declaring Typesafe Enums Revisited

An enum type can be declared as a top-level type. Enum types can also be nested, but only within other static members, or other top-level type declarations (Section 8.2, p. 355). When nested, it is implicitly static, and can be declared with the keyword static. The following skeletal code shows the two enum types Day and Meal declared as static members in the class MealPrices:

public class MealPrices {
  public enum Day { /* ... */ }                          // Static member

  public static enum Meal { /* ... */ }                  // Static member

  public static void main(String[] args) { /* ... */ }   // Static method
}

An enum type cannot be explicitly extended using the extends clause. An enum type is implicitly final, unless it contains constant-specific class bodies. If it declares constant-specific class bodies, it is implicitly extended. No matter what, it cannot be explicitly declared final.

An enum type cannot be declared abstract, regardless of whether each abstract method is overridden in the constant-specific class body of every enum constant.

Like a class, an enum can implement interfaces.

public interface ITimeInfo {
  public int getHour();
  public int getMins();
}

public enum Meal implements ITimeInfo {
  // ...
  public int getHour() { return this.hh; }
  public int getMins() { return this.mm; }
}

The Java Collections Framework provides a special purpose set implementation (java.util.EnumSet) and a special purpose map implementation (java.util.EnumMap) for use with enum types. These special purpose implementations provide better performance for enum types than the general purpose counterparts, and are worth checking out.

Review Questions

Review Questions

3.7 Which statements about the enum type are true?

Select the three correct answers.

(a) An enum type is a subclass of the abstract class java.lang.Enum, hence it is Comparable and Serializable.

(b) An enum type can implement interfaces.

(c) We can instantiate an enum type using the new operator.

(d) An enum type can define constructors.

(e) We can explicitly use the extend clause to extend an enum type.

(f) Enum types do not inherit members from the Object class.

3.8 What will be the result of attempting to compile and run the following code?

public enum Drill {
  ATTENTION("Attention!"), EYES_RIGHT("Eyes right!"),
  EYES_LEFT("Eyes left!"), AT_EASE("At ease!");

  private String command;

  Drill(String command) {
    this.command = command;
  }

  public static void main(String[] args) {
    System.out.println(ATTENTION);             // (1)
    System.out.println(AT_EASE);               // (2)
  }
}

Select the one correct answer.

(a) The code compiles, but reports a ClassNotFoundException when run, since an enum type cannot be run as a standalone application.

(b) The compiler reports errors in (1) and (2), as the constants must be qualified by the enum type name Drill.

(c) The compiler reports errors in (1) and (2), as the constants cannot be accessed in a static context.

(d) The code compiles and prints:

ATTENTION
AT_EASE

(e) The code compiles and prints:

Attention!
At ease!

(f) None of the above.

3.9 What will be the result of compiling and running the following code?

import java.util.Arrays;

public enum Priority {
  ONE(1) { public String toString() { return "LOW"; } },        // (1)
  TWO(2),
  THREE(3) { public String toString() { return "NORMAL"; } },   // (2)
  FOUR(4),
  FIVE(5) { public String toString() { return "HIGH"; } };      // (3)

  private int pValue;

  Priority(int pValue) {
    this.pValue = pValue;
  }

  public static void main(String[] args) {
    System.out.println(Arrays.toString(Priority.values()));
  }
}

Select the one correct answer.

(a) The code compiles, but reports a ClassNotFoundException when run, since an enum type cannot be run as a standalone application.

(b) The compiler reports syntax errors in (1), (2), and (3).

(c) The code compiles and prints:

[LOW, TWO, NORMAL, FOUR, HIGH]

(d) The code compiles and prints:

[ONE, TWO, THREE, FOUR, HIGH]

(e) None of the above.

3.10 Which statement about the following program is true?

public enum Scale {
  GOOD('C'), BETTER('B'), BEST('A'),

  private char grade;

  Scale(char grade) {
    this.grade = grade;
  }
  abstract public char getGrade();

  public static void main (String[] args) {
    System.out.println (GOOD.getGrade());     // (1)
  }
}

Select the one correct answer.

(a) Since the enum type declares an abstract method, the enum type must be declared as abstract.

(b) The method call GOOD.getGrade() in (1) can be written without the enum type name.

(c) An enum type cannot declare an abstract method.

(d) An enum type can declare an abstract method, but each enum constant must provide an implementation.

3.11 What will be the result of compiling and running the following code?

public enum TrafficLight {
  RED("Stop"), YELLOW("Caution"), GREEN("Go");

  private String action;

  TrafficLight(String action) {
    this.action = action;
  }

  public static void main(String[] args) {
    TrafficLight green = new TrafficLight("Go");
    System.out.println(GREEN.equals(green));
  }
}

Select the one correct answer.

(a) The code will compile and print: true.

(b) The code will compile and print: false.

(c) The code will not compile, as an enum type cannot be instantiated.

(d) An enum type does not have the equals() method.

3.12 Given the following program:

public enum Scale2 {
  GOOD('C')   { public char getGrade() { return grade; } },
  BETTER('B') { public char getGrade() { return grade; } },
  BEST('A')   { public char getGrade() { return grade; } };

  private char grade;

  Scale2(char grade) {
    this.grade = grade;
  }
  // (1) INSERT CODE HERE

  public static void main (String[] args) {
    System.out.println(GOOD.getGrade());
  }
}

Which code, when inserted at (1), will make the program print C?

Select the two correct answers.

(a) public char getGrade() { return grade; }

(b) public int getGrade() { return grade; }

(c) abstract public int getGrade();

(d) abstract public char getGrade();

3.13 Given the following program:

enum Scale3 {
  GOOD(Grade.C), BETTER(Grade.B), BEST(Grade.A);

  enum Grade {A, B, C}
  private Grade grade;

  Scale3(Grade grade) {
    this.grade = grade;
  }

  public Grade getGrade() { return grade; }
}

public class Scale3Client {
  public static void main (String[] args) {
    System.out.println(/* (1) INSERT CODE HERE */);
  }
}

Which code, when inserted at (1), will make the program print true?

Select the four correct answers.

(a) Scale3.GOOD.getGrade() != Scale3.Grade.C

(b) Scale3.GOOD.getGrade().compareTo(Scale3.Grade.C) != 0

(c) Scale3.GOOD.getGrade().compareTo(Scale3.Grade.A) > 0

(d) Scale3.GOOD.compareTo(Scale3.BEST) > 0

(e) Scale3.GOOD.getGrade() instanceof Scale3.Grade

(f) Scale3.GOOD instanceof Scale3

(g) Scale3.GOOD.getGrade().toString().equals(Scale3.Grade.C.toString())

3.14 What will be the result of compiling and running the following code?

public enum Scale5 {
  GOOD, BETTER, BEST;

  public char getGrade() {
    char grade = 'u0000';
    switch(this){
      case GOOD:   grade = 'C'; break;
      case BETTER: grade = 'B'; break;
      case BEST:   grade = 'A'; break;
    }
    return grade;
  }

  public static void main (String[] args) {
    System.out.println(GOOD.getGrade());
  }
}

Select the one correct answer.

(a) The program will not compile, as the switch expression is not compatible with the case labels.

(b) The program will not compile, as enum constants cannot be used as case labels.

(c) The case labels must be qualified with the enum type name.

(d) The program compiles, and when run, prints: C

(e) The program compiles, and when run, prints: GOOD

(f) None of the above.

3.15 Given the following code:

package p1;
enum March {LEFT, RIGHT}                          // (1)
public class Defence {
  enum March {LEFT, RIGHT}                        // (2)
  static enum Military {
    INFANTRY, AIRFORCE;
    enum March {LEFT, RIGHT}                      // (3)
  }
  class Secret {
    enum March {LEFT, RIGHT}                      // (4)
  }
  static class Open {
    enum March {LEFT, RIGHT}                      // (5)
  }
  public static void declareWar() {

    enum March {LEFT, RIGHT}                      // (6)
  }
  public void declarePeace() {
    enum March {LEFT, RIGHT}                      // (7)
  }
}

Which enum declarations are not legal?

Select the three correct answers.

(a) The enum declaration at (1) is not legal.

(b) The enum declaration at (2) is not legal.

(c) The enum declaration at (3) is not legal.

(d) The enum declaration at (4) is not legal.

(e) The enum declaration at (5) is not legal.

(f) The enum declaration at (6) is not legal.

(g) The enum declaration at (7) is not legal.

3.16 Given the following code:

public enum Direction {
  EAST, WEST, NORTH, SOUTH;

  public static void main (String[] args) {
    // (1) INSERT LOOP HERE
  }
}

Which loops, when inserted independently at (1), will give the following output:

EAST
WEST
NORTH
SOUTH

Select the three correct answers.

(a) for (Direction d : Direction.values()) {
  System.out.println(d);
}

(b) for (Direction d : Direction.values()) {
  System.out.println(d.name());
}

(c) for (String name : Direction.names()) {
  System.out.println(name);
}

(d) for (Direction d : java.util.Arrays.asList(Direction.values())) {
  System.out.println(d);
}

(e) for (Direction d : java.util.Arrays.asList(Direction.class)) {
  System.out.println(d);
};

3.17 What will be the result of compiling and running the following code?

enum Rank {
  FIRST(20), SECOND(0), THIRD(8);
  Rank(int value) {
    System.out.print(value);
  }
}
public class EnumCreation {
  public static void main (String[] args) {
    System.out.println(" " + Rank.values().length);
  }
}

Select the one correct answer.

(a) The program will compile and print:

3

(b) The program will compile and print:

2008
3

(c) The program will compile. When run, it will print:

2008

and throw an exception.

(d) None of the above.

3.6 Arrays

An array is a data structure that defines an indexed collection of a fixed number of homogeneous data elements. This means that all elements in the array have the same data type. A position in the array is indicated by a non-negative integer value called the index. An element at a given position in the array is accessed using the index. The size of an array is fixed and cannot be changed.

In Java, arrays are objects. Arrays can be of primitive data types or reference types. In the former case, all elements in the array are of a specific primitive data type. In the latter case, all elements are references of a specific reference type. References in the array can then denote objects of this reference type or its subtypes. Each array object has a final field called length, which specifies the array size, i.e., the number of elements the array can accommodate. The first element is always at index 0 and the last element at index n-1, where n is the value of the length field in the array.

Simple arrays are one-dimensional arrays, that is, a simple list of values. Since arrays can store reference values, the objects referenced can also be array objects. Thus, multi-dimensional arrays are implemented as array of arrays.

Passing array references as parameters is discussed in Section 3.7. Type conversions for array references on assignment and on method invocation are discussed in Section 7.7, p. 317.

Declaring Array Variables

A one-dimensional array variable declaration has either the following syntax:

<element type>[] <array name>;

or

<element type> <array name>[];

where <element type> can be a primitive data type or a reference type. The array variable <array name> has the type <element type>[]. Note that the array size is not specified. This means that the array variable <array name> can be assigned the reference value of an array of any length, as long as its elements have <element type>.

It is important to understand that the declaration does not actually create an array. It only declares a reference that can refer to an array object.

int anIntArray[], oneInteger;
Pizza[] mediumPizzas, largePizzas;

The two declarations above declare anIntArray and mediumPizzas to be reference variables that can refer to arrays of int values and arrays of Pizza objects, respectively. The variable largePizzas can denote an array of pizzas, but the variable oneInteger cannot denote an array of int values—it is a simple variable of the type int.

The [] notation can also be specified after a variable name to declare it as an array variable, but then it only applies to this variable.

An array variable that is declared as a member of a class, but is not initialized to any array, will be initialized to the default reference value null. This default initialization does not apply to local reference variables and, therefore, does not apply to local array variables either (see Section 2.4, p. 33). This should not be confused with initialization of the elements of an array during array construction.

Constructing an Array

An array can be constructed for a fixed number of elements of a specific type, using the new operator. The reference value of the resulting array can be assigned to an array variable of the corresponding type. The syntax of the array creation expression is shown on the right-hand side of the following assignment statement:

<array name> = new <element type>[<array size>];

The minimum value of <array size> is 0, in other words, zero-length arrays can be constructed in Java. If the array size is negative, a NegativeArraySizeException is thrown.

Given the following array declarations:

int anIntArray[], oneInteger;
Pizza[] mediumPizzas, largePizzas;

the arrays can be constructed as follows:

anIntArray   = new int[10];          // array for 10 integers
mediumPizzas = new Pizza[5];         // array of 5 pizzas
largePizzas  = new Pizza[3];         // array of 3 pizzas

The array declaration and construction can be combined.

<element type1>[] <array name> = new <element type2>[<array size>];

Here the array type <element type2>[] must be assignable to the array type <element type1>[] (Section 7.7, p. 317). When the array is constructed, all its elements are initialized to the default value for <element type2>. This is true for both member and local arrays when they are constructed.

In all examples below, the code constructs the array, and the array elements are implicitly initialized to their default value. For example, all elements of the array anIntArray get the value 0, and all element of the array mediumPizzas get the value null when the arrays are constructed.

int[] anIntArray = new int[10];                  // Default element value: 0.

Pizza[] mediumPizzas = new Pizza[5];             // Default element value: null.

// Pizza class extends Object class
Object[] objArray = new Pizza[3];                // Default element value: null.

// Pizza class implements Eatable interface
Eatable[] eatables = new Pizza[2];               // Default element value: null.

The value of the field length in each array is set to the number of elements specified during the construction of the array; for example, mediumPizzas.length has the value 5.

Once an array has been constructed, its elements can also be explicitly initialized individually; for example, in a loop. The examples in the rest of this section make use of a loop to traverse the elements of an array for various purposes.

Initializing an Array

Java provides the means of declaring, constructing, and explicitly initializing an array in one declaration statement:

<element type>[] <array name> = { <array initialize list> };

This form of initialization applies to member as well as local arrays. The <array initialize list> is a comma-separated list of zero or more expressions. Such an array initialization block results in the construction and initialization of the array.

int[] anIntArray = {13, 49, 267, 15, 215};

The array anIntArray is declared as an array of ints. It is constructed to hold 5 elements (equal to the length of the list of expressions in the block), where the first element is initialized to the value of the first expression (13), the second element to the value of the second expression (49), and so on.

// Pizza class extends Object class
Object[] objArray = { new Pizza(), new Pizza(), null };

The array objArray is declared as an array of the Object class, constructed to hold three elements. The initialization code sets the first two elements of the array to refer to two Pizza objects, while the last element is initialized to the null reference. Note that the number of objects created in the above declaration statement is actually three: the array object with three references and the two Pizza objects.

The expressions in the <array initialize list> are evaluated from left to right, and the array name obviously cannot occur in any of the expressions in the list. In the examples above, the <array initialize list> is terminated by the right curly bracket, }, of the block. The list can also be legally terminated by a comma. The following array has length two, and not three:

Topping[] pizzaToppings = { new Topping("cheese"), new Topping("tomato"), };

The declaration statement at (1) in the following code defines an array of four String objects, while the declaration statement at (2) shows that a String object is not the same as an array of char.

// Array with 4 String objects:
String[] pets = {"crocodiles", "elephants", "crocophants", "elediles"}; // (1)

// Array of 3 characters:
char[] charArray = {'a', 'h', 'a'};    // (2) Not the same as "aha".

Using an Array

The array object is referenced by the array name, but individual array elements are accessed by specifying an index with the [] operator. The array element access expression has the following syntax:

<array name> [<index expression>]

Each individual element is treated as a simple variable of the element type. The index is specified by the <index expression>, which can be any expression that evaluates to a non-negative int value. Since the lower bound of an array is always 0, the upper bound is one less than the array size, that is, <array name>.length-1. The ith element in the array has index (i-1). At runtime, the index value is automatically checked to ensure that it is within the array index bounds. If the index value is less than 0, or greater than or equal to <array name>.length, an ArrayIndexOutOfBoundsException is thrown. A program can either check the index explicitly or catch the exception (see Section 6.5, p. 235), but an illegal index is typically an indication of a program bug.

In the array element access expression, the <array name> can be any expression that returns a reference to an array. For example, the following expression returns the character 'H' at index 1 in the character array returned by a call to the toCharArray() method of the String class: "AHA".toCharArray()[1].

The array operator [] is used to declare array types (Topping[]), specify array size (new Topping[3]), and to access array elements (toppings[1]). This operator is not used when the array reference is manipulated, for example, in an array reference assignment (see Section 7.9, p. 320), or when the array reference is passed as an actual parameter in a method call (see Section 3.7, p. 86).

Example 3.7 shows traversal of arrays. The loop at (3) initializes the local array trialArray declared at (2) five times with pseudo-random numbers (from 0.0 to 100.0), by calling the method randomize() declared at (5). The minimum value in the array is found by calling the method findMinimum() declared at (6), and is stored in the array storeMinimum declared at (1). The loop at (4) prints the minimum values from the trials. The start value of the loop variable is initially set to 0. The loop condition tests whether the loop variable is less than the length of the array; this guarantees that the index will not go out of bounds.

Example 3.7 Using Arrays

public class Trials {
  public static void main(String[] args) {
    // Declare and construct the local arrays:
    double[] storeMinimum = new double[5];               // (1)
    double[] trialArray = new double[15];                // (2)
    for (int i = 0; i < storeMinimum.length; ++i) {      // (3)

      // Initialize the array.
      randomize(trialArray);

      // Find and store the minimum value.
      storeMinimum[i] = findMinimum(trialArray);
    }

    // Print the minimum values:                            (4)
    for (int i = 0; i < storeMinimum.length; ++i)
      System.out.printf("%.4f%n", storeMinimum[i]);
  }

  public static void randomize(double[] valArray) {      // (5)
    for (int i = 0; i < valArray.length; ++i)
      valArray[i] = Math.random() * 100.0;
  }

  public static double findMinimum(double[] valArray) {  // (6)
    // Assume the array has at least one element.
    double minValue = valArray[0];
    for (int i = 1; i < valArray.length; ++i)
      minValue = Math.min(minValue, valArray[i]);
    return minValue;
  }
}

Possible output from the program:

6.9330
2.7819
6.7427
18.0849
26.2462

Anonymous Arrays

As shown earlier in this section, the following declaration statement

<element type1>[] <array name> = new <element type2>[<array size>]; // (1)
int[] intArray = new int[5];

can be used to construct arrays using an array creation expression. The size of the array is specified in the array creation expression, which creates the array and initializes the array elements to their default values. On the other hand, the following declaration statement

<element type>[] <array name> = { <array initialize list> };           // (2)
int[] intArray = {3, 5, 2, 8, 6};

both creates the array and initializes the array elements to specific values given in the array initializer block. However, the array initialization block is not an expression.

Java has another array creation expression, called anonymous array, which allows the concept of the array creation expression from (1) and the array initializer block from (2) to be combined, to create and initialize an array object:

new <element type>[] { <array initialize list> }
new int[] {3, 5, 2, 8, 6}

The construct has enough information to create a nameless array of a specific type. Neither the name of the array nor the size of the array is specified. The construct returns the reference value of the newly-created array, which can be assigned to references and passed as argument in method calls. In particular, the following two examples of declaration statements are equivalent.

int[] intArray = {3, 5, 2, 8, 6};        // (1)
int[] intArray = new int[] {3, 5, 2, 8, 6}; // (2)

In (1), an array initializer block is used to create and initialize the elements. In (2), an anonymous array expression is used. It is tempting to use the array initialization block as an expression; for example, in an assignment statement, as a short cut for assigning values to array elements in one go. However, this is illegal—instead, an anonymous array expression should be used.

int[] daysInMonth;
daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Not ok.
daysInMonth = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // ok.

The concept of anonymous arrays is similar to that of anonymous classes (see Section 8.5, p. 377): they both combine the definition and the creation of objects into one operation.

In Example 3.8, an anonymous array is constructed at (1), and passed as a parameter to the static method findMinimum() defined at (2). Note that no array name or array size is specified for the anonymous array.

Example 3.8 Using Anonymous Arrays

public class AnonArray {
  public static void main(String[] args) {
    System.out.println("Minimum value: " +
        findMinimum(new int[] {3, 5, 2, 8, 6}));                   // (1)
  }

  public static int findMinimum(int[] dataSeq) {                   // (2)
    // Assume the array has at least one element.
    int min = dataSeq[0];
    for (int index = 1; index < dataSeq.length; ++index)
      if (dataSeq[index] < min)
        min = dataSeq[index];
    return min;
  }
}

Output from the program:

Minimum value: 2

Multidimensional Arrays

Since an array element can be an object reference and arrays are objects, array elements can themselves reference other arrays. In Java, an array of arrays can be defined as follows:

<element type>[][]...[] <array name>;

or

<element type> <array name>[][]...[];

In fact, the sequence of square bracket pairs, [], indicating the number of dimensions, can be distributed as a postfix to both the element type and the array name. Arrays of arrays are also often called multidimensional arrays.

The following declarations are all equivalent:

int[][] mXnArray;      // 2-dimensional array
int[]   mXnArray[];    // 2-dimensional array
int     mXnArray[][];  // 2-dimensional array

It is customary to combine the declaration with the construction of the multidimensional array.

int[][] mXnArray = new int[4][5];    // 4 x 5 matrix of ints

The previous declaration constructs an array mXnArray of four elements, where each element is an array (row) of 5 int values. The concept of rows and columns is often used to describe the dimensions of a 2-dimensional array, which is often called a matrix. However, such an interpretation is not dictated by the Java language.

Each row in the previous matrix is denoted by mXnArray[i], where 0i < 4. Each element in the ith row, mXnArray[i], is accessed by mXnArray[i][j], where 0j < 5. The number of rows is given by mXnArray.length, in this case 4, and the number of values in the ith row is given by mXnArray[i].length, in this case 5 for all the rows, where 0i < 4.

Multidimensional arrays can also be constructed and explicitly initialized using array initializer blocks discussed for simple arrays. Note that each row is an array which uses an array initializer block to specify its values:

double[][] identityMatrix = {
  {1.0, 0.0, 0.0, 0.0 }, // 1. row
  {0.0, 1.0, 0.0, 0.0 }, // 2. row
  {0.0, 0.0, 1.0, 0.0 }, // 3. row
  {0.0, 0.0, 0.0, 1.0 }  // 4. row
}; // 4 x 4 Floating-point matrix

Arrays in a multidimensional array need not have the same length, and are often called ragged arrays. The array of arrays pizzaGalore in the code below will have five rows, the first four rows have different lengths but the fifth row is left unconstructed.

Pizza[][] pizzaGalore = {
 { new Pizza(), null, new Pizza() },    // 1. row is an array of 3 elements.
 { null, new Pizza()},                  // 2. row is an array of 2 elements.
 new Pizza[1],                          // 3. row is an array of 1 element.
 {},                                    // 4. row is an array of 0 elements.
 null                                   // 5. row is not constructed.
};

When constructing multidimensional arrays with the new operator, the length of the deeply nested arrays may be omitted. In which case, these arrays are left unconstructed. For example, an array of arrays to represent a room on a floor in a hotel on a street in a city can have the type HotelRoom[][][][]. From left to right, the square brackets represent indices for street, hotel, floor, and room, respectively. This 4-dimensional array of arrays can be constructed piecemeal, starting with the leftmost dimension and proceeding to the rightmost.

HotelRoom[][][][] rooms = new HotelRoom[10][5][][];  // Just streets and hotels.

The above declaration constructs the array of arrays rooms partially with ten streets, where each street has five hotels. Floors and rooms can be added to a particular hotel on a particular street:

rooms[0][0]       = new HotelRoom[3][]; // 3 floors in 1st. hotel on 1st. street.
rooms[0][0][0]    = new HotelRoom[8];   // 8 rooms on 1st. floor in this hotel.
rooms[0][0][0][0] = new HotelRoom();    // Initializes 1st. room on this floor.

The code below constructs an array of arrays matrix, where the first row has one element, the second row has two elements, and the third row has three elements. Note that the outer array is constructed first. The second dimension is constructed in a loop that constructs the array in each row. The elements in the multidimensional array will be implicitly initialized to the default double value (0.0D). In Figure 3.2, the array of arrays matrix is depicted after the elements have been explicitly initialized.

double[][] matrix = new double[3][];      // No. of rows.

for (int i = 0; i < matrix.length; ++i)
   matrix[i] = new double[i + 1];         // Construct a row.

Figure 3.2 Array of Arrays

Array of Arrays

Two other ways of initializing such an array of arrays are shown below. The first one uses array initializer blocks, and the second one uses an anonymous array of arrays.

double[][] matrix2 = {    // Using array initializer blocks.
  {0.0},                  // 1. row
  {0.0, 0.0},             // 2. row
  {0.0, 0.0, 0.0}         // 3. row
}

double[][] matrix3 = new double[][] { // Using an anonymous array of arrays.
  {0.0},                  // 1. row
  {0.0, 0.0},             // 2. row
  {0.0, 0.0, 0.0}         // 3. row
}

The type of the variable matrix is double[][], i.e., a two-dimensional array of double values. The type of the variable matrix[i] (where 0i<matrix.length) is double[], i.e., a one-dimensional array of double values. The type of the variable matrix[i][j] (where 0i<matrix.length and 0j<matrix[i].length) is double, i.e., a simple variable of type double.

Nested loops are a natural match for manipulating multidimensional arrays. In Example 3.9, a rectangular 4 × 3 int matrix is declared and constructed at (1). The program finds the minimum value in the matrix. The outer loop at (2) traverses the rows (mXnArray[i], where 0i<mXnArray.length), and the inner loop at (3) traverses the elements in each row in turn (mXnArray[i][j], where 0j<mXnArray[i].length). The outer loop is executed mXnArray.length times, or 4 times, and the inner loop is executed (mXnArray.length) × (mXnArray[i].length), or 12 times, since all rows have the same length, 3.

The for(:) loop also provides a safe and convenient way of traversing an array, and ample examples are provided in Section 6.3, p. 220.

The Java standard library also provides the class java.util.Arrays that contains various static methods for manipulating arrays, such as sorting and searching (see Section 15.11, p. 842).

Example 3.9 Using Multidimensional Arrays

public class MultiArrays {

  public static void main(String[] args) {
    // Declare and construct the M X N matrix.
    int[][] mXnArray = {                                           // (1)
        {16,  7, 12}, // 1. row
        { 9, 20, 18}, // 2. row
        {14, 11,  5}, // 3. row
        { 8,  5, 10}  // 4. row
    }; // 4 x 3 int matrix

    // Find the minimum value in a M X N matrix:
    int min = mXnArray[0][0];
    for (int i = 0; i < mXnArray.length; ++i)                      // (2)
      // Find min in mXnArray[i], i.e. in the row given by index i:
      for (int j = 0; j < mXnArray[i].length; ++j)                 // (3)
        min = Math.min(min, mXnArray[i][j]);

    System.out.println("Minimum value: " + min);
  }
}

Output from the program:

Minimum value: 5

Review Questions

Review Questions

3.18 Given the following declaration, which expression returns the size of the array, assuming the array has been initialized?

int[] array;

Select the one correct answer.

(a) array[].length()

(b) array.length()

(c) array[].length

(d) array.length

(e) array[].size()

(f) array.size()

3.19 Is it possible to create arrays of length zero?

Select the one correct answer.

(a) Yes, you can create arrays of any type with length zero.

(b) Yes, but only for primitive data types.

(c) Yes, but only for arrays of reference types.

(d) No, you cannot create zero-length arrays, but the main() method may be passed a zero-length array of Strings when no program arguments are specified.

(e) No, it is not possible to create arrays of length zero in Java.

3.20 Which one of the following array declaration statements is not legal?

Select the one correct answer.

(a) int []a[] = new int [4][4];

(b) int a[][] = new int [4][4];

(c) int a[][] = new int [][4];

(d) int []a[] = new int [4][];

(e) int [][]a = new int [4][4];

3.21 Which of these array declaration statements are not legal?

Select the two correct answers.

(a) int[] i[] = { { 1, 2 }, { 1 }, {}, { 1, 2, 3 } };

(b) int i[] = new int[2] {1, 2};

(c) int i[][] = new int[][] { {1, 2, 3}, {4, 5, 6} };

(d) int i[][] = { { 1, 2 }, new int[ 2 ] };

(e) int i[4] = { 1, 2, 3, 4 };

3.22 What would be the result of compiling and running the following program?

// Filename: MyClass.java
class MyClass {
  public static void main(String[] args) {
    int size = 20;
    int[] arr = new int[ size ];

    for (int i = 0; i < size; ++i) {
      System.out.println(arr[i]);
    }
  }
}

Select the one correct answer.

(a) The code will not compile, because the array type int[] is incorrect.

(b) The program will compile, but will throw an ArrayIndexOutOfBoundsException when run.

(c) The program will compile and run without error, but will produce no output.

(d) The program will compile and run without error, and will print the numbers 0 through 19.

(e) The program will compile and run without error, and will print 0 twenty times.

(f) The program will compile and run without error, and will print null twenty times.

3.23 What would be the result of compiling and running the following program?

public class DefaultValuesTest {
  int[] ia = new int[1];
  boolean b;
  int i;
  Object o;

  public static void main(String[] args) {
    DefaultValuesTest instance = new DefaultValuesTest();
    instance.print();
  }

  public void print() {
    System.out.println(ia[0] + " " + b + " " + i + " " + o);
  }
}

Select the one correct answer.

(a) The program will fail to compile because of uninitialized variables.

(b) The program will throw a java.lang.NullPointerException when run.

(c) The program will print: 0 false NaN null.

(d) The program will print: 0 false 0 null.

(e) The program will print: null 0 0 null.

(f) The program will print: null false 0 null.

3.7 Parameter Passing

Objects communicate by calling methods on each other. A method call is used to invoke a method on an object. Parameters in the method call provide one way of exchanging information between the caller object and the callee object (which need not be different).

Defining methods is discussed in Section 3.3, p. 44. Invoking static methods on classes is discussed in Section 4.10, p. 147.

The syntax of a method call can be any one of the following:

<object reference>.<method name> (<actual parameter list>)
<class name>.<static method name> (<actual parameter list>)
<method name> (<actual parameter list>)

The <object reference> must be an expression that evaluates to a reference value denoting the object on which the method is called. If the caller and the callee are the same, <object reference> can be omitted (see discussion on the this reference in Section 3.3, p. 45). The <class name> can be the fully qualified name (see Section 4.2, p. 105) of the class. The <actual parameter list> is comma-separated if there is more than one parameter. The parentheses are mandatory even if the actual parameter list is empty. This distinguishes the method call from field access. One can specify fully qualified names for classes and packages using the dot operator.

objRef.doIt(time, place);         // Explicit object reference
int i = java.lang.Math.abs(-1);   // Fully qualified class name
int j = Math.abs(-1);             // Simple class name
someMethod(ofValue);              // Object or class implicitly implied
someObjRef.make().make().make();  // make() returns a reference value

The dot operator ('.') has left associativity. In the last code line, the first call of the make() method returns a reference value that denotes the object on which to execute the next call, and so on. This is an example of call chaining.

Each actual parameter (also called an argument) is an expression that is evaluated, and whose value is passed to the method when the method is invoked. Its value can vary from invocation to invocation. Formal parameters are parameters defined in the method declaration (see Section 3.3, p. 44) and are local to the method (see Section 2.4, p. 36).

In Java, all parameters are passed by value, that is, an actual parameter is evaluated and its value is assigned to the corresponding formal parameter. Table 3.1 summarizes the value that is passed depending on the type of the formal parameter. In the case of primitive data types, the data value of the actual parameter is passed. If the actual parameter is a reference to an object (i.e., instantiation of a class, enum, or array), the reference value is passed and not the object itself. If the actual parameter is an array element of a primitive data type, its data value is passed, and if the array element is a reference to an object, then its reference value is passed.

Table 3.1 Parameter Passing By Value

Parameter Passing By Value

It should also be stressed that each invocation of a method has its own copies of the formal parameters, as is the case for any local variables in the method (Section 6.5, p. 235).

The order of evaluation in the actual parameter list is always from left to right. The evaluation of an actual parameter can be influenced by an earlier evaluation of an actual parameter. Given the following declaration:

int i = 4;

the method call

leftRight(i++, i);

is effectively the same as

leftRight(4, 5);

and not as

leftRight(4, 4);

Section 5.2, p. 164, provides an overview of conversions that can take place in a method invocation context. Method invocation conversions for primitive values are discussed in the next subsection (p. 82), and those for reference types are discussed in Section 7.10, p. 323. Calling variable arity methods and generic methods is discussed in Section 3.8, p. 90, and in Section 14.8, p. 697, respectively.

For the sake of simplicity, the examples in subsequent sections primarily show method invocation on the same object or the same class. The parameter passing mechanism is no different when different objects or classes are involved.

Passing Primitive Data Values

An actual parameter is an expression that is evaluated first and the resulting value is then assigned to the corresponding formal parameter at method invocation. The use of this value in the method has no influence on the actual parameter. In particular, when the actual parameter is a variable of a primitive data type, the value of the variable is copied to the formal parameter at method invocation. Since formal parameters are local to the method, any changes made to the formal parameter will not be reflected in the actual parameter after the call completes.

Legal type conversions between actual parameters and formal parameters of primitive data types are summarized here from Table 5.1, p. 163:

widening primitive conversion

• unboxing conversion, followed by an optional widening primitive conversion

These conversions are illustrated by invoking the following method

static void doIt(long i) { /* ... */ }

with the following code:

Integer intRef = 34;
Long longRef = 34L;
doIt(34);         // (1) Primitive widening conversion: long <-- int
doIt(longRef);    // (2) Unboxing: long <-- Long
doIt(intRef);     // (3) Unboxing, followed by primitive widening conversion:
                  //     long <-- int <-- Integer

However, for parameter passing, there are no implicit narrowing conversions for integer constant expressions (see Section 5.2, p. 164).

Example 3.10 Passing Primitive Values

public class CustomerOne {
  public static void main (String[] args) {
    PizzaFactory pizzaHouse = new PizzaFactory();
    int pricePrPizza = 15;
    double totPrice = pizzaHouse.calcPrice(4, pricePrPizza);       // (1)
    System.out.println("Value of pricePrPizza: " + pricePrPizza);  // Unchanged.
  }
}

class PizzaFactory {
  public double calcPrice(int numberOfPizzas, double pizzaPrice) {  // (2)
    pizzaPrice = pizzaPrice/2.0;       // Changes price.
    return numberOfPizzas * pizzaPrice;
  }
}

Output from the program:

Value of pricePrPizza: 15

In Example 3.10, the method calcPrice() is defined in the class PizzaFactory at (2). It is called from the CustomerOne.main() method at (1). The value of the first actual parameter, 4, is copied to the int formal parameter numberOfPizzas. Note that the second actual parameter pricePrPizza is of the type int, while the corresponding formal parameter pizzaPrice is of the type double. Before the value of the actual parameter pricePrPizza is copied to the formal parameter pizzaPrice, it is implicitly widened to a double. The passing of primitive values is illustrated in Figure 3.3.

Figure 3.3 Parameter Passing: Primitive Data Values

Parameter Passing: Primitive Data Values

The value of the formal parameter pizzaPrice is changed in the calcPrice() method, but this does not affect the value of the actual parameter pricePrPizza on return. It still has the value 15. The bottom line is that the formal parameter is a local variable, and changing its value does not affect the value of the actual parameter.

Passing Reference Values

If the actual parameter expression evaluates to a reference value, the resulting reference value is assigned to the corresponding formal parameter reference at method invocation. In particular, if an actual parameter is a reference to an object, the reference value stored in the actual parameter is passed. This means that both the actual parameter and the formal parameter are aliases to the object denoted by this reference value during the invocation of the method. In particular, this implies that changes made to the object via the formal parameter will be apparent after the call returns.

Type conversions between actual and formal parameters of reference types are discussed in Section 7.10, p. 323.

Example 3.11 Passing Reference Values

public class CustomerTwo {
  public static void main (String[] args) {
    Pizza favoritePizza = new Pizza();              // (1)
    System.out.println("Meat on pizza before baking: " + favoritePizza.meat);
    bake(favoritePizza);                            // (2)
    System.out.println("Meat on pizza after baking: " + favoritePizza.meat);
  }
  public static void bake(Pizza pizzaToBeBaked) {   // (3)
    pizzaToBeBaked.meat = "chicken";  // Change the meat on the pizza.
    pizzaToBeBaked = null;                          // (4)
  }
}

class Pizza {                                       // (5)
  String meat = "beef";
}

Output from the program:

Meat on pizza before baking: beef
Meat on pizza after baking: chicken

In Example 3.11, a Pizza object is created at (1). Any object of the class Pizza created using the class declaration at (5) always results in a beef pizza. In the call to the bake() method at (2), the reference value of the object referenced by the actual parameter favoritePizza is assigned to the formal parameter pizzaToBeBaked in the declaration of the bake() method at (3).

One particular consequence of passing reference values to formal parameters is that any changes made to the object via formal parameters will be reflected back in the calling method when the call returns. In this case, the reference favoritePizza will show that chicken has been substituted for beef on the pizza. Setting the formal parameter pizzaToBeBaked to null at (4) does not change the reference value in the actual parameter favoritePizza. The situation at method invocation, and just before return from method bake(), is illustrated in Figure 3.4.

Figure 3.4 Parameter Passing: Reference Values

Parameter Passing: Reference Values

In summary, the formal parameter can only change the state of the object whose reference value was passed to the method.

The parameter passing strategy in Java is call-by-value and not call-by-reference, regardless of the type of the parameter. Call-by-reference would have allowed values in the actual parameters to be changed via formal parameters; that is, the value in pricePrPizza to be halved in Example 3.10 and favoritePizza to be set to null in Example 3.11. However, this cannot be directly implemented in Java.

Passing Arrays

The discussion on passing reference values in the previous section is equally valid for arrays, as arrays are objects in Java. Method invocation conversions for array types are discussed along with those for other reference types in Section 7.10, p. 323.

Example 3.12 Passing Arrays

public class Percolate {

  public static void main (String[] args) {
    int[] dataSeq = {6,4,8,2,1};    // Create and initialize an array.

    // Write array before percolation:
    printIntArray(dataSeq);

    // Percolate:
    for (int index = 1; index < dataSeq.length; ++index)
      if (dataSeq[index-1] > dataSeq[index])
        swap(dataSeq, index-1, index);                          // (1)

    // Write array after percolation:
    printIntArray(dataSeq);
  }

  public static void swap(int[] intArray, int i, int j) {       // (2)
    int tmp = intArray[i]; intArray[i] = intArray[j]; intArray[j] = tmp;
  }

  public static void swap(int v1, int v2) {                     // (3)
    int tmp = v1; v1 = v2; v2 = tmp;
  }

  public static void printIntArray(int[] array) {               // (4)
    for (int value : array)
      System.out.print(" " + value);
    System.out.println();
  }
}

Output from the program:

6 4 8 2 1
4 6 2 1 8

In Example 3.12, the idea is to repeatedly swap neighboring elements in an integer array until the largest element in the array percolates to the last position in the array.

Note that in the declaration of the method swap() at (2), the formal parameter intArray is of the array type int[]. The swap() method is called in the main() method at (1), where one of the actual parameters is the array variable dataSeq. The reference value of the array variable dataSeq is assigned to the array variable intArray at method invocation. After return from the call to the swap() method, the array variable dataSeq will reflect the changes made to the array via the corresponding formal parameter. This situation is depicted in Figure 3.5 at the first call and return from the swap() method, indicating how values of elements at indices 0 and 1 in the array have been swapped.

Figure 3.5 Parameter Passing: Arrays

Parameter Passing: Arrays

However, the declaration of the swap() method at (3) will not swap two values. The method call

swap(dataSeq[index-1], dataSeq[index]);

will have no effect on the array elements, as the swapping is done on the values of the formal parameters.

The method printIntArray() at (4) also has a formal parameter of array type int[]. Note that the formal parameter is specified as an array reference using the [] notation, but this notation is not used when an array is passed as an actual parameter.

Array Elements as Actual Parameters

Array elements, like other variables, can store values of primitive data types or reference values of objects. In the latter case, it means they can also be arrays, i.e., arrays of arrays (see Section 3.6, p. 74). If an array element is of a primitive data type, its data value is passed, and if it is a reference to an object, the reference value is passed. The method invocation conversions apply to the values of array elements as well.

Example 3.13 Array Elements as Primitive Data Values

public class FindMinimum {

  public static void main(String[] args) {
    int[] dataSeq = {6,4,8,2,1};

    int minValue = dataSeq[0];
    for (int index = 1; index < dataSeq.length; ++index)
      minValue = minimum(minValue, dataSeq[index]);            // (1)

    System.out.println("Minimum value: " + minValue);
  }

  public static int minimum(int i, int j) {                    // (2)
    return (i <= j) ? i : j;
  }
}

Output from the program:

Minimum value: 1

In Example 3.13, note that the value of all but one element of the array dataSeq is retrieved and passed consecutively at (1) to the formal parameter j of the minimum() method defined at (2). The discussion in Section 3.7 on call-by-value also applies to array elements that have primitive values.

Example 3.14 Array Elements as Reference Values

public class FindMinimumMxN {

  public static void main(String[] args) {
    int[][] matrix = { {8,4},{6,3,2},{7} };                  // (1)

    int min = findMinimum(matrix[0]);                        // (2)
    for (int i = 1; i < matrix.length; ++i) {
      int minInRow = findMinimum(matrix[i]);                 // (3)
      if (min > minInRow) min = minInRow;
    }
    System.out.println("Minimum value in matrix: " + min);
  }

  public static int findMinimum(int[] seq) {                 // (4)
    int min = seq[0];
    for (int i = 1; i < seq.length; ++i)

      min = Math.min(min, seq[i]);
    return min;
  }
}

Output from the program:

Minimum value in matrix: 2

In Example 3.14, note that the formal parameter seq of the findMinimum() method defined at (4) is an array variable. The variable matrix denotes an array of arrays declared at (1) simulating a multidimensional array, which has three rows, where each row is a simple array. The first row, denoted by matrix[0], is passed to the findMinimum() method in the call at (2). Each remaining row is passed by its reference value in the call to the findMinimum() method at (3).

final Parameters

A formal parameter can be declared with the keyword final preceding the parameter declaration in the method declaration. A final parameter is also known as a blank final variable; that is, it is blank (uninitialized) until a value is assigned to it, (e.g., at method invocation) and then the value in the variable cannot be changed during the lifetime of the variable (see also the discussion in Section 4.10, p. 148). The compiler can treat final variables as constants for code optimization purposes. Declaring parameters as final prevents their values from being changed inadvertently. Whether a formal parameter is declared as final, does not affect the caller’s code.

The declaration of the method calcPrice() from Example 3.10 is shown below, with the formal parameter pizzaPrice declared as final.

public double calcPrice(int numberOfPizzas, final double pizzaPrice) {  // (2')
  pizzaPrice = pizzaPrice/2.0;                       // (3) Not allowed.
  return numberOfPizzas * pizzaPrice;
}

If this declaration of the calcPrice() method is compiled, the compiler will not allow the value of the final parameter pizzaPrice to be changed at (3) in the body of the method.

As another example, the declaration of the method bake() from Example 3.11 is shown below, with the formal parameter pizzaToBeBaked declared as final.

public static void bake(final Pizza pizzaToBeBaked) { // (3)
  pizzaToBeBaked.meat = "chicken";                    // (3a) Allowed.
  pizzaToBeBaked = null;                              // (4) Not allowed.
}

If this declaration of the bake() method is compiled, the compiler will not allow the reference value of the final parameter pizzaToBeBaked to be changed at (4) in the body of the method. Note that this applies to the reference value in the final parameter, not the object denoted by this parameter. The state of the object can be changed as before, as shown at (3a).

3.8 Variable Arity Methods

A fixed arity method must be called with the same number of actual parameters (also called arguments) as the number of formal parameters specified in its declaration. If the method declaration specifies two formal parameters, every call of this method must specify exactly two arguments. We say that the arity of this method is 2. In other words, the arity of such a method is fixed, and it is equal to the number of formal parameters specified in the method declaration.

Java also allows declaration of variable arity methods; meaning that the number of arguments in its call can be varied. As we shall see, invocations of such a method may contain more actual parameters than formal parameters. Variable arity methods are heavily employed in formatting text representation of values (see Section 12.7, p. 593). The variable arity method System.out.printf() is used in many examples for this purpose.

The last formal parameter in a variable arity method declaration is declared as follows:

<type>... <formal parameter name>

The ellipsis (...) is specified between the <type> and the <formal parameter name>. The <type> can be a primitive type, a reference type, or a type parameter. Whitespace can be specified on both sides of the ellipsis. Such a parameter is usually called a varargs parameter.

Apart from the varargs parameter, a variable arity method is identical to a fixed arity method. The method publish() below is a variable arity method:

public static void publish(int n, String... data) {      // (int, String[])
  System.out.println("n: " + n + ", data size: " + data.length);
}

The varargs parameter in a variable arity method is always interpreted as having the type:

<type>[]

In the body of the publish() method, the varargs parameter data has the type String[], i.e., a simple array of Strings.

Only one varargs parameter is permitted in the formal parameter list, and it is always the last parameter in the formal parameter list. Given that the method declaration has n formal parameters, and the method call has k actual parameters, k must be equal to or greater than n-1. The last k-n+1 actual parameters are evaluated and stored in an array whose reference value is passed as the value of the actual parameter. In the case of the publish() method, n is equal to 2, so k can be 1, 2, 3, and so on. The following invocations of the publish() method show what arguments are passed in each method call:

publish(1);                  // (1, new String[] {})
publish(2, "two");           // (2, new String[] {"two"})
publish(3, "two", "three");  // (3, new String[] {"two", "three"})

Each method call results in an implicit array being created, and passed as argument. This array can contain zero or more argument values that do not correspond to the formal parameters preceding the varargs parameter. This array is referenced by the varargs parameter data in the method declaration. The calls above would result in the publish() method printing:

n: 1, data size: 0
n: 2, data size: 1
n: 3, data size: 2

Calling a Varargs Method

Example 3.15 illustrates various aspects of calling a varargs method. The method flexiPrint() in the VarargsDemo class has a varargs parameter:

public static void flexiPrint(Object... data) { // Object[]
  //...
}

The varargs method prints the name of the Class object representing the actual array that is passed. It prints the number of elements in this array, and also the text representation of each element in the array.

The method flexiPrint() is called in the main() method. First with the values of primitive types and Strings ((1) to (8)), then it is called with the program arguments supplied in the command line, ((9) to (11)).

Compiling the program results in a warning, which we ignore for the time being. The program can still be run, as shown in Example 3.15. The numbers at the end of the lines in the output relate to numbers in the code, and are not printed by the program.

Example 3.15 Calling a Varargs Method

public class VarargsDemo {

  public static void flexiPrint(Object... data) { // Object[]
    // Print the name of the Class object for the varargs parameter.
    System.out.print(" Type: " + data.getClass().getName());

    System.out.println("  No. of elements: " + data.length);

    for(int i = 0; i < data.length; i++)

      System.out.print(data[i] + " ");
    if (data.length != 0)
      System.out.println();
  }

  public static void main(String... args) {
    int    day   = 1;
    String month = "March";
    int    year  = 2009;

    // Passing primitives and non-array types.
    flexiPrint();                        // (1) new Object[] {}
    flexiPrint(day);                     // (2) new Object[] {new Integer(day)}
    flexiPrint(day, month);              // (3) new Object[] {new Integer(day),
                                         //                   month}
    flexiPrint(day, month, year);        // (4) new Object[] {new Integer(day),
                                         //                   month,
                                         //                   new Integer(year)}

    // Passing an array type.
    Object[] dateInfo = {day,            // (5) new Object[] {new Integer(day),
                         month,          //                   month,
                         year};          //                   new Integer(year)}
    flexiPrint(dateInfo);                // (6) Non-varargs call
    flexiPrint((Object) dateInfo);       // (7) new Object[] {(Object) dateInfo}
    flexiPrint(new Object[] {dateInfo}); // (8) Non-varargs call

    // Explicit varargs or non-varargs call.
    flexiPrint(args);                    // (9) Warning!
    flexiPrint((Object) args);           // (10) Explicit varargs call.
    flexiPrint((Object[]) args);         // (11) Explicit non-varargs call
  }
}

Compiling the program:

>javac VarargsDemo.java
VarargsDemo.java:39: warning: non-varargs call of varargs method with inexact
argument type for last parameter;
cast to java.lang.Object for a varargs call
cast to java.lang.Object[] for a non-varargs call and to suppress this warning
    flexiPrint(args);                    // (10) Warning!
               ^
1 warning

Running the program:

>java VarargsDemo To arg or not to arg

Type: [Ljava.lang.Object;  No. of elements: 0                (1)

Type: [Ljava.lang.Object;  No. of elements: 1                (2)
1

Type: [Ljava.lang.Object;  No. of elements: 2                (3)
1 March

Type: [Ljava.lang.Object;  No. of elements: 3                (4)
1 March 2009

Type: [Ljava.lang.Object;  No. of elements: 3                (6)
1 March 2009

Type: [Ljava.lang.Object;  No. of elements: 1                (7)
[Ljava.lang.Object;@1eed786

Type: [Ljava.lang.Object;  No. of elements: 1                (8)
[Ljava.lang.Object;@1eed786

Type: [Ljava.lang.String;  No. of elements: 6                (9)
To arg or not to arg

Type: [Ljava.lang.Object;  No. of elements: 1                (10)
[Ljava.lang.String;@187aeca

Type: [Ljava.lang.String;  No. of elements: 6                (11)
To arg or not to arg

Varargs and Non-Varargs Method Calls

The calls in (1) to (4) are all varargs calls, as an implicit Object array is created, in which the values of the actual parameters are stored. The reference value of this array is passed to the method. The printout shows that the type of the parameter is actually an array of Objects ([Ljava.lang.Object;).

The call at (6) is different from the previous calls, in that the actual parameter is an array that has the same type (Object[]) as the varargs parameter, without having to create an implicit array. In such a case, no implicit array is created, and the reference value of the array dateInfo is passed to the method. See also the result from this call at (6) in the output. The call at (6) is a non-varargs call, where no implicit array is created:

flexiPrint(dateInfo);                // (6) Non-varargs call

However, if the actual parameter is cast to the type Object as in (7), a varargs call is executed:

flexiPrint((Object) dateInfo);       // (7) new Object[] {(Object) dateInfo}

The type of the actual argument is now not the same as that of the varargs parameter, resulting in an array of the type Object[] being created, in which the array dateInfo is stored as an element. The printout at (7) shows that only the text representation of the dateInfo array is printed, and not its elements, as it is the sole element of the implicit array.

The call at (8) is a non-varargs call, for the same reason as the call in (6), but now the array dateInfo is explicitly stored as an element in an array of the type Object[] that matches the type of the varargs parameter:

flexiPrint(new Object[] {dateInfo}); // (8) Non-varargs call

The compiler issues a warning for the call at (9):

flexiPrint(args);                    // (9) Warning!

The actual parameter args is an array of the type String[], which is a subtype of Object[]—the type of the varargs parameter. The array args can be passed in a nonvarargs call as an array of the type String[], or in a varargs call as an element in an implicitly created array of the type Object[]. Both calls are feasible and valid in this case. Note that the compiler chooses a non-varargs call rather than a varargs call, but also issues a warning. The result at (9) confirms this course of action.

The array args of the type String[] is explicitly passed as an Object in a varargs call at (10), similar to the call at (7):

flexiPrint((Object) args);           // (10) Explicit varargs call.

The array args of type String[] is explicitly passed as an array of the type Object[] in a non-varargs call at (11). This call is equivalent to the call at (9), where the widening reference conversion is implicit, but now without a warning at compile time. The two calls print the same information, as evident from the output at (9) and (11):

flexiPrint((Object[]) args);         // (11) Explicit non-varargs call

The compiler will complain if an attempt is made to overload the method flexiPrint() in the class VarargsDemo, as shown in the following code:

public static void flexiPrint(Object... data) {  }  // Compile-time error!
public static void flexiPrint(Object[] data) {  }   // Compile-time error!

These declarations would result in two methods with equivalent signatures in the same class, if this was permitted. Overloading and overriding of methods with varargs is discussed in Section 7.10, p. 324. The implications that generics have for varargs are discussed in Section 14.13, p. 729.

3.9 The main() Method

The mechanics of compiling and running Java applications using the JDK are outlined in Section 1.10. The java command executes a method called main in the class specified on the command line. Any class can have a main() method, but only the main() method of the class specified in the java command is executed to start a Java application.

The main() method must have public accessibility so that the interpreter can call this method (see Section 4.9, p. 138). It is a static method belonging to the class, so that no object of the class is required to start the execution (see Section 4.10, p. 147).

It does not return a value, that is, it is declared void (see Section 6.4, p. 228). It always has an array of String objects as its only formal parameter. This array contains any arguments passed to the program on the command line (see p. 95). The following method header declarations fit the bill, and any one of them can be used for the main() method:

public static void main(String[] args)    // Method header
public static void main(String... args)   // Method header

The above requirements do not exclude specification of additional modifiers (see Section 4.10, p. 146) or any throws clause (see Section 6.9, p. 257). The main() method can also be overloaded like any other method (see Section 3.3, p. 47). The JVM ensures that the main() method having the above method header is the starting point of program execution.

Program Arguments

Any arguments passed to the program on the command line can be accessed in the main() method of the class specified on the command line:

>java Colors red green blue

These arguments are called program arguments. Note that the command name, java, and the class name Colors are not passed to the main() method of the class Colors, nor are any other options that are specified in the command line.

Since the formal parameter of the main() method is an array of String objects, individual String elements in the array can be accessed by using the [] operator.

In Example 3.16, the three arguments "red", "green", and "blue" can be accessed in the main() method of the Colors class as args[0], args[1], and args[2], respectively. The total number of arguments is given by the field length of the String array args. Note that program arguments can only be passed as strings, and must be explicitly converted to other values by the program, if necessary.

When no arguments are specified on the command line, an array of zero String elements is created and passed to the main() method. This means that the reference value of the formal parameter in the main() method is never null.

Program arguments supply information to the application, which can be used to tailor the runtime behavior of the application according to user requirements.

Example 3.16 Passing Program Arguments

public class Colors {
  public static void main(String[] args) {
    System.out.println("No. of program arguments: " + args.length);
    for (int i = 0; i < args.length; i++)
      System.out.println("Argument no. " + i + " (" + args[i] + ") has " +
                          args[i].length() + " characters.");
  }
}

Running the program:

>java Colors red green blue
No. of program arguments: 3
Argument no. 0 (red) has 3 characters.
Argument no. 1 (green) has 5 characters.
Argument no. 2 (blue) has 4 characters.

Review Questions

Review Questions

3.24 What will be printed when the following program is run?

public class ParameterPass {
  public static void main(String[] args) {
    int i = 0;
    addTwo(i++);
    System.out.println(i);
  }

  static void addTwo(int i) {
    i += 2;
  }
}

Select the one correct answer.

(a) 0

(b) 1

(c) 2

(d) 3

3.25 What will be the result of compiling and running the following program?

public class Passing {
  public static void main(String[] args) {
    int a = 0; int b = 0;
    int[] bArr = new int[1]; bArr[0] = b;

    inc1(a); inc2(bArr);

    System.out.println("a=" + a + " b=" + b + " bArr[0]=" + bArr[0]);
  }

  public static void inc1(int x) { x++; }

  public static void inc2(int[] x) { x[0]++; }
}

Select the one correct answer.

(a) The code will fail to compile, since x[0]++; is not a legal statement.

(b) The code will compile and will print "a=1 b=1 bArr[0]=1", when run.

(c) The code will compile and will print "a=0 b=1 bArr[0]=1", when run.

(d) The code will compile and will print "a=0 b=0 bArr[0]=1", when run.

(e) The code will compile and will print "a=0 b=0 bArr[0]=0", when run.

3.26 Which statements, when inserted at (1), will cause a compilation error?

public class ParameterUse {
  static void main(String[] args) {
    int a = 0;
    final int b = 1;
    int[] c = { 2 };
    final int[] d = { 3 };
    useArgs(a, b, c, d);
  }

  static void useArgs(final int a, int b, final int[] c, int[] d) {
    // (1) INSERT STATEMENT HERE.
  }
}

Select the two correct answers.

(a) a++;

(b) b++;

(c) b = a;

(d) c[0]++;

(e) d[0]++;

(f) c = d;

3.27 Which method declarations are valid declarations?

Select the three correct answers.

(a) void compute(int... is) { }

(b) void compute(int is...) { }

(c) void compute(int... is, int i, String... ss) { }

(d) void compute(String... ds) { }

(e) void compute(String... ss, int len) { }

(f) void compute(char[] ca, int... is) { }

3.28 Given the following code:

public class RQ800_40 {
  static void print(Object... obj) {
    System.out.println("Object...: " + obj[0]);
  }
  public static void main(String[] args) {
    // (1) INSERT METHOD CALL HERE.
  }
}

Which method call, when inserted at (1), will not result in the following output from the program:

Object...: 9

Select the one correct answer.

(a) print("9", "1", "1");

(b) print(9, 1, 1);

(c) print(new int[] {9, 1, 1});

(d) print(new Integer[] {9, 1, 1});

(e) print(new String[] {"9", "1", "1"});

(f) print(new Object[] {"9", "1", "1"});

3.29 What will be the result of compiling and running the following program?

public class RQ800_20 {
  static void compute(int... is) {                             // (1)
    System.out.print("|");
    for(int i : is) {
      System.out.print(i + "|");
    }
    System.out.println();
  }
  static void compute(int[] ia, int... is) {                   // (2)
    compute(ia);
    compute(is);
  }
  static void compute(int[] inta, int[]... is) {               // (3)
    for(int[] ia : is) {
      compute(ia);
    }
  }
  public static void main(String[] args) {
    compute(new int[] {10, 11}, new int[] {12, 13, 14});       // (4)
    compute(15, 16);                                           // (5)
    compute(new int[] {17, 18}, new int[][] {{19}, {20}});     // (6)
    compute(null, new int[][] {{21}, {22}});                   // (7)
  }
}

Select the one correct answer.

(a) The program does not compile because of errors in one or more calls to the compute() method.

(b) The program compiles, but throws a NullPointerException when run.

(c) The program compiles and prints:

|10|11|
|12|13|14|
|15|16|
|19|
|20|
|21|
|22|

(d) The program compiles and prints:

|12|13|14|
|15|16|
|10|11|
|19|
|20|
|21|
|22|

3.30 Which of these method declarations are valid declarations of the main() method that would be called by the JVM in order to start the execution of a Java application?

Select the three correct answers.

(a) static void main(String[] args) { /* ... */ }

(b) public static int main(String[] args) { /* ... */ }

(c) public static void main(String args) { /* ... */ }

(d) final public static void main(String[] arguments) { /* ... */ }

(e) public int main(Strings[] args, int argc) { /* ... */ }

(f) static public void main(String args[]) { /* ... */ }

(g) static public void main(String... args) { /* ... */ }

3.31 Which of the following are reserved keywords?

Select the three correct answers.

(a) public

(b) static

(c) void

(d) main

(e) String

(f) args

3.32 Given the class

// File name: Args.java
public class Args {
  public static void main(String[] args) {
    System.out.println(args[0] + " " + args[args.length-1]);
  }
}

what would be the result of executing the following command line?

>java Args In politics stupidity is not a handicap

Select the one correct answer.

(a) The program will throw an ArrayIndexOutOfBoundsException.

(b) The program will print "java handicap".

(c) The program will print "Args handicap".

(d) The program will print "In handicap".

(e) The program will print "Args a".

(f) The program will print "In a".

3.33 Which statement about the following program is true?

class MyClass {
  public static void main(String[] args) {
    String[] numbers = { "one", "two", "three", "four" };

    if (args.length == 0) {

     System.out.println("no arguments");
    } else {
     System.out.println(numbers[ args.length ] + " arguments");
    }
  }
}

Select the one correct answer.

(a) The program will fail to compile.

(b) The program will throw a NullPointerException when run with no program arguments.

(c) The program will print "no arguments" and "two arguments" when called with zero and three program arguments, respectively.

(d) The program will print "no arguments" and "three arguments" when called with zero and three program arguments, respectively.

(e) The program will print "no arguments" and "four arguments" when called with zero and three program arguments, respectively.

(f) The program will print "one arguments" and "four arguments" when called with zero and three program arguments, respectively.

Chapter Summary

Chapter Summary

The following information was included in this chapter:

• overview of declarations that can be specified in a class

• understanding pattern names for properties and the event model in the JavaBeans standard

defining methods, usage of the this reference in an instance method, and method overloading

• defining constructors, usage of the default constructor, and overloading of constructors

• declaring and using enum types, and extending them implicitly

• explanation of declaration, construction, initialization, and usage of both one-and multi-dimensional arrays, including anonymous arrays

• parameter passing, both primitive values and object references, including arrays and array elements; and declaring final parameters

• declaring and calling methods with varargs

• declaration of the main() method whose execution starts the application

• passing program arguments to the main() method

Programming Exercises

Programming Exercises

3.1 Imagine you are creating an application that has a number of different tools a user may invoke. These tools need a special context to work in. The context describes the current active selection in the application. The selection consists of a reference to an arbitrary object. We wish to create a JavaBean representing an editing context that the tools may use. The JavaBean should contain the afore-mentioned selection reference. We do not want to allow direct manipulation of the reference, but want to have methods in the editing context that allow anyone to get and set the current selection.

Write such a JavaBean. Be sure to get the accessibility right.

3.2 Write a program to grade a short multiple-choice quiz. The correct answers for the quiz are:

1. C

2. A

3. B

4. D

5. B

6. C

7. C

8. A

Assume that the pass marks are 5 out of 8. The program stores the correct answers in an array. The submitted answers are specified as program arguments. Let X represent a question that was not answered on the quiz. Use an enum type to represent the result of answering a question.

The program calculates and prints a report along the following lines:

Question  Submitted Ans. Correct Ans.  Result
    1         C              C        CORRECT
    2         B              A          WRONG
    3         B              B        CORRECT
    4         D              D        CORRECT
    5         B              B        CORRECT
    6         C              C        CORRECT
    7         A              C          WRONG
    8         X              A     UNANSWERED
No. of correct answers:      5
No. of wrong answers:        2
No. of questions unanswered: 1
The candidate PASSED.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.134.218