Overloaded Constructors – Declarations

Overloaded Constructors

Like methods, constructors can 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 explicit implementation of the no-argument constructor at (1) and that of a non-zero argument constructor at (2). The constructors are overloaded, as is evident by their signatures. The non-zero argument constructor at (2) is called when an object of the class Light is created at (4), and the no-argument constructor is likewise called at (3). Overloading of constructors allows appropriate initialization of objects on creation, depending on the constructor invoked (see chaining of constructors in §5.3, p. 209). It is recommended to use the @param tag in a Javadoc comment to document the formal parameters of a constructor.

Click here to view code image

class Light {
  // …
  // No-argument constructor:
  Light() {                                                  // (1)
    noOfWatts = 50;
    indicator = true;
    location  = “X”;
  }
  // Non-zero argument constructor:
  Light(int noOfWatts, boolean indicator, String location) { // (2)
    this.noOfWatts = noOfWatts;
    this.indicator = indicator;
    this.location  = location;
  }
  //…
}
class Greenhouse {
  // …
  Light firstLight = new Light();                        // (3) OK. Calls (1)
  Light moreLight  = new Light(100, true, “Greenhouse”); // (4) OK. Calls (2)
}

3.8 Static Member Declarations

In this section we look at static members in classes, but in general, the keyword static is used in the following contexts:

Local Variable Scope in the switch Body – Control Flow

Local Variable Scope in the switch Body

The scope of a local variable declared in a switch statement or a switch expression is the entire switch block. Any local block in the switch body introduces a new local scope. Any local variable declared in it has block scope, and therefore, is only accessible in that block. A local variable declared in an enclosing local scope cannot be redeclared in a nested local scope (§6.6, p. 354).

Summary of the switch Statement and the switch Expression

Table 4.1 summarizes the features of the switch statement and the switch expression, and provides a comparison of the two constructs.

Table 4.1 Comparing the switch Statement and the switch Expression

NotationThe switch statementThe switch expression
The colon (:) notation: case label: statementsExecutes statements associated with the matching case label.Fall-through can occur.No compile-time check for exhaustiveness.Only break and return statements allowed to control fall-through.Executes statements associated with the matching case label, but must have a yield statement to return a value.Fall-through can occur.Compile-time check for exhaustiveness.No break or return statement allowed.
The arrow (->) notation: case label -> actionAction associated with a switch rule can be an expression statement, can be a block, or can throw an exception.Mutually exclusive switch rules: no fall-through can occur.No compile-time check for exhaustiveness.break and return statements allowed.Action associated with a switch rule can be any expression, can be a block, or can throw an exception.Mutually exclusive switch rules: no fall-through can occur.Compile-time check for exhaustiveness.No break or return statement allowed.Must return a value that is either the value of a stand-alone expression or the value of the expression in a yield statement that can occur as the last statement in a block.

Anonymous Arrays – Declarations

Anonymous Arrays

As shown earlier in this section, the following declaration statement can be used to construct arrays using an array creation expression:

Click here to view code image

element_type
1
[]
array_name
 = new
element_type
2
[
array_size
];   // (1)

int[] intArray = new int[5];

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. By comparison, the following declaration statement both creates the array and initializes the array elements to specific values given in the array initializer:

Click here to view code image

element_type
[]
array_name
 = {
array_initialize_list
 }; // (2)

int[] intArray = {3, 5, 2, 8, 6};

However, the array initializer is not an expression. Java has another array creation expression, called an anonymous array, which allows the concept of the array creation expression from (1) to be combined with the array initializer from (2), so as to create and initialize an array:

Click here to view code image

new
element_type
[] {
array_initialize_list
 }

new int[] {3, 5, 2, 8, 6}

This construct has enough information to create a nameless array of a specific type and specific length. 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 arguments in method calls. In particular, the following declaration statements are equivalent:

Click here to view code image

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

At (1), an array initializer is used to create and initialize the elements. At (2), an anonymous array expression is used. It is tempting to use the array initializer as an expression—for example, in an assignment statement, as a shortcut for assigning values to array elements in one go. However, this is not allowed; instead, an anonymous array expression should be used. The concept of the anonymous array combines the definition and the creation of the array into one operation.

Click here to view code image

int[] daysInMonth;
daysInMonth = {31, 28, 31, 30, 31, 30,
               31, 31, 30, 31, 30, 31};                   // Compile-time error
daysInMonth = new int[] {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // OK

In Example 3.8, an anonymous array is constructed at (1), and passed as an actual 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

Click here to view code image

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

Constructing an Array – Declarations

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:

Click here to view code image

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 at runtime.

Given the declarations

Click here to view code image

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

the three arrays in the declarations can be constructed as follows:

Click here to view code image

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.

Click here to view code image

element_type
1
[]
array_name
 = new
element_type
2
[
array_size
];

In the preceding syntax, the array type element_type2[] must be assignable to the array type element_type1[] (§5.8, p. 261). When the array is constructed, all of 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 the following examples, the code constructs the array, and the array elements are implicitly initialized to their default values. For example, all elements of the array anIntArray get the value 0, and all elements of the array mediumPizzas get the value null when the arrays are constructed.

Click here to view code image

int[] anIntArray = new int[10];                  // Default element value: 0
Pizza[] mediumPizzas = new Pizza[5];             // 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 iterate over the elements of an array for various purposes.

The switch Statement with the Arrow (->) Notation – Control Flow

The switch Statement with the Arrow (->) Notation

The form of the switch statement with the arrow notation is shown in Figure 4.4. This form defines switch rules in which each case label is associated with a corresponding action using the arrow (->) notation.

Figure 4.4 Form of the switch Statement with the Arrow Notation

switch (
selector_expression
) {
  // Switch block with switch rules defined using arrow notation:
  case
CC
                  ->
expression_statement
;
  case
CC
1
,
CC
2
, …,
CC
m
  ->
block

  case
CC
4
                 ->
throw_statement

  …
  default                  -> …
}

Compared to the switch statement with the colon notation (Figure 4.2), there are a few things to note.

First, although the case labels (and the default label) are specified similarly, the arrow notation does not allow multiple case labels to be associated with a common action. However, the same result can be achieved by specifying a single case label with a list of case constants, thereby associating the case constants with a common action.

Second, the action that can be associated with the case labels in switch rules is restricted. The switch statement with the colon notation allows a group of statements, but the switch statement with the arrow notation only allows the following actions to be associated with case labels:

By far, the canonical action of a case label in a switch rule is an expression statement. Such an expression statement is always terminated by a semicolon (;). Typically, the value returned by the expression statement is discarded. In the examples below, what is important is the side effect of evaluating the expression statements.

Click here to view code image


case PASSED -> ++numbersPassed;
case FAILED -> ++numbersFailed;

A block of statements can be used if program logic should be refined.

Click here to view code image


case ALARM ->  { soundTheAlarm();
                 callTheFireDepartment(); }

The switch rule below throws an exception when the value of the selector expression does not match any case constants:

Click here to view code image


default -> throw new IllegalArgumentException(“Not a valid value”);

Third, the execution of the switch rules is mutually exclusive (Figure 4.5). Once the action in the switch rule has completed execution, the execution of the switch statement terminates. This is illustrated in Figure 4.5 where only one expression statement is executed, after which the switch statement also terminates. There is no fall-through and the break statement is not necessary.

Figure 4.5 Activity Diagram for the switch Statement with the Arrow Notation

Example 4.3 is a refactoring of Example 4.2 with a switch statement with the arrow notation. At (2), (3), (4), and (8), the action executed is an expression statement, whereas at (5), the action executed is a block. Using switch rules results in compact and elegant code that also improves the readability of the switch statement.

Example 4.3 Nested switch Statements with the Arrow Notation

Click here to view code image

public class SeasonsII {
  public static void main(String[] args) {
    int monthNumber = 11;
    switch(monthNumber) {                                             // (1) Outer
      case 12, 1,  2 -> System.out.println(“Snow in the winter.”);    // (2)
      case 3,  4,  5 -> System.out.println(“Green grass in the spring.”);   // (3)
      case 6,  7,  8 -> System.out.println(“Sunshine in the summer.”);      // (4)
      case 9, 10, 11 -> {                                             // (5)
        switch(monthNumber) { // Nested switch                           (6) Inner
          case 10 -> System.out.println(“Halloween.”);
          case 11 -> System.out.println(“Thanksgiving.”);
        }
        // Always printed for case constants 9, 10, 11:
        System.out.println(“Yellow leaves in the fall.”);             // (7)
      }
      default -> throw new IllegalArgumentException(monthNumber +
                                            ” is not a valid month.”);// (8)
    }
  }
}

Output from the program:

Click here to view code image

Thanksgiving.
Yellow leaves in the fall.

Multidimensional Arrays – Declarations

Multidimensional Arrays

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

Click here to view code image

element_type
[][]…[]
array_name;

or

Click here to view code image

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 often called multidimensional arrays.

The following declarations are all equivalent:

Click here to view code image

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

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

Click here to view code image

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 five int values. The concept of rows and columns is often used to describe the dimensions of a two-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 0 ≤ i < 4. Each element in the ith row, mXnArray[i], is accessed by mXnArray[i][j], where 0 ≤ j < 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 0 ≤ i < 4.

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

Click here to view code image

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; in which case, they are called ragged arrays. The array of arrays pizzaGalore in the following code has five rows; the first four rows have different lengths but the fifth row is left unconstructed:

Click here to view code image

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 such a case, these arrays are left unconstructed. For example, an array of arrays to represent a room (defined by class HotelRoom) 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 four-dimensional array of arrays can be constructed piecemeal, starting with the leftmost dimension and proceeding to the rightmost successively.

Click here to view code image

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

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

Click here to view code image

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 next code snippet 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.1, the array of arrays matrix is depicted after the elements have been explicitly initialized.

Click here to view code image

double[][] matrix = new double[3][];      // (1) Number of rows.

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

Figure 3.1 Array of Arrays

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

Two other ways of initializing such an array of arrays are shown next. The first approach uses array initializers, and the second uses an anonymous array of arrays.

Click here to view code image

double[][] matrix2 = {    // (2) Using array initializers.
  {1.0},                  // 1. row
  {1.0, 2.0},             // 2. row
  {1.0, 2.0, 3.0}         // 3. row
};

double[][] matrix3 = new double[][] { // (3) Using an anonymous array of arrays.
  {1.0},                  // 1. row
  {1.0, 2.0},             // 2. row
  {1.0, 2.0, 1.0}         // 3. row
};

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) iterates over the rows (mXnArray[i], where 0 ≤ i< mXnArray.length), and the inner loop at (3) iterates over the elements in each row in turn (mXnArray[i][j], where 0 ≤ j< mXnArray[i].length). The outer loop is executed mXnArray.length times, or four 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 iterating over an array. Several examples of its use are provided in §4.8, p. 176.

Example 3.9 Using Multidimensional Arrays

Click here to view code image

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 an M X N matrix:
    int min = mXnArray[0][0];
    for (int i = 0; i < mXnArray.length; ++i)                      // (2)
      // Find min in mXnArray[i], 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

Passing Primitive Data Values – Declarations

Passing Primitive Data Values

An actual parameter is an expression that is evaluated first, with the resulting value then being 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 from the stack 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 2.17, p. 47:

  • Primitive widening conversion
  • Unboxing conversion, followed by an optional widening primitive conversion

These conversions are illustrated by invoking the following method

Click here to view code image

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

with the following code:

Click here to view code image

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 (§2.4, p. 48).

Example 3.10 Passing Primitive Values

Click here to view code image

public class CustomerOne {
  public static void main (String[] args) {
    PizzaFactory pizzaHouse = new PizzaFactory();
    int pricePrPizza = 15;
    System.out.println(“Value of pricePrPizza before call: ” + pricePrPizza);
    double totPrice = pizzaHouse.calcPrice(4, pricePrPizza);             // (1)
    System.out.println(“Value of pricePrPizza after call: ” + pricePrPizza);
  }
}
class PizzaFactory {
  public double calcPrice(int numberOfPizzas, double pizzaPrice) {       // (2)
    pizzaPrice = pizzaPrice / 2.0;       // Changes price.
    System.out.println(“Changed pizza price in the method: ” + pizzaPrice);
    return numberOfPizzas * pizzaPrice;
  }
}

Output from the program:

Click here to view code image

Value of pricePrPizza before call: 15
Changed pizza price in the method: 7.5
Value of pricePrPizza after call: 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.2.

Figure 3.2 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.

Array Elements as Actual Parameters – Declarations

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, they can also be arrays—that is, arrays of arrays (p. 124). If an array element is of a primitive data type, its data value is passed; 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

Click here to view code image

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, 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 on passing primitive values (p. 129) also applies to array elements that have primitive values.

In Example 3.14, 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 that 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).

Example 3.14 Array Elements as Reference Values

Click here to view code image

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)
      min = Math.min(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

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 §6.6, p. 352). The compiler can treat final variables as constants for code optimization purposes. Declaring parameters as final prevents their values from being changed inadvertently. A formal parameter’s declaration as final does not affect the caller’s code.

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

Click here to view code image

public double calcPrice(int numberOfPizzas, final double pizzaPrice) {  // (2′)
  pizzaPrice = pizzaPrice/2.0;        // (3) Not allowed. Compile-time error!
  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 here, with the formal parameter pizzaToBeBaked declared as final:

Click here to view code image

public static void bake(final Pizza pizzaToBeBaked) { // (3)
  pizzaToBeBaked.meat = “chicken”;   // (3a) Allowed
  pizzaToBeBaked = null;             // (4) Not allowed. Compile-time error!
}

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, but not to the object denoted by this parameter. The state of the object can be changed as before, as shown at (3a).

For use of the final keyword in other contexts, see §5.5, p. 225.

Selection Statements – Control Flow

4.1 Selection Statements

Java provides selection statements that allow the program to choose between alternative actions during execution. The choice is based on criteria specified in the selection statement. These selection statements are

  • The simple if statement
  • The if-else statement
  • The switch statement and the switch expression

The Simple if Statement

The simple if statement has the following syntax:

if (
condition
)
statement

It is used to decide whether an action is to be performed or not, based on a condition. The action to be performed is specified by statement, which can be a single statement or a code block. The condition must evaluate to a boolean or Boolean value. In the latter case, the Boolean value is unboxed to the corresponding boolean value.

The semantics of the simple if statement are straightforward. The condition is evaluated first. If its value is true, statement (called the if block) is executed and then execution continues with the rest of the program. If the value is false, the if block is skipped and execution continues with the rest of the program. The semantics are illustrated by the activity diagram in Figure 4.1a.

Figure 4.1 Activity Diagram for if Statements

In the following examples of the if statement, it is assumed that the variables and the methods have been appropriately defined:

Click here to view code image

if (emergency)            // emergency is a boolean variable
  operate();
if (temperature > critical)
  soundAlarm();
if (isLeapYear() && endOfCentury())
  celebrate();
if (catIsAway()) {        // Block
  getFishingRod();
  goFishing();
}

Note that statement can be a block, and the block notation is necessary if more than one statement is to be executed when the condition is true.

Since the condition evaluates to a boolean value, it avoids a common programming error: using an expression of the form (a=b) as the condition, where inadvertently an assignment operator is used instead of a relational operator. The compiler will flag this as an error, unless both a and b are boolean.

Note that the if block can be any valid statement. In particular, it can be the empty statement (;) or the empty block ({}). A common programming error is inadvertent use of the empty statement.

Click here to view code image

if (emergency); // Empty if block
  operate();    // Executed regardless of whether it was an emergency

Calling a Variable Arity Method – Declarations

Calling a Variable Arity Method

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

Click here to view code image

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

The variable arity method prints the name of the Class object representing the actual array that is passed at runtime. It prints the number of elements in this array as well as the text representation of each element in the array.

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

Compiling the program results in a warning at (9), 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 Variable Arity Method

Click here to view code image

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);
    System.out.print(“Element values: “);
    for(Object element : data)
      System.out.print(element + ” “);
    System.out.println();
  }
  public static void main(String… args) {
    int    day       = 13;
    String monthName = “August”;
    int    year      = 2009;
    // Passing primitives and non-array types:
    flexiPrint();                      // (1) new Object[] {}
    flexiPrint(day);                   // (2) new Object[] {Integer.valueOf(day)}
    flexiPrint(day, monthName);        // (3) new Object[] {Integer.valueOf(day),
                                       //                   monthName}
    flexiPrint(day, monthName, year);  // (4) new Object[] {Integer.valueOf(day),
                                       //                   monthName,
                                       //                   Integer.valueOf(year)}
    System.out.println();
    // Passing an array type:
    Object[] dateInfo = {day,          // (5) new Object[] {Integer.valueOf(day),
                         monthName,    //                   monthName,
                         year};        //                   Integer.valueOf(year)}
    flexiPrint(dateInfo);              // (6) Non-varargs call
    flexiPrint((Object) dateInfo);     // (7) new Object[] {(Object) dateInfo}
    flexiPrint(new Object[]{dateInfo});// (8) Non-varargs call
    System.out.println();
    // 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:

Click here to view code image

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

Running the program:

Click here to view code image

>
java VarargsDemo To arg or not to arg

Type: [Ljava.lang.Object;  No. of elements: 0                (1)
Element values:
Type: [Ljava.lang.Object;  No. of elements: 1                (2)
Element values: 13
Type: [Ljava.lang.Object;  No. of elements: 2                (3)
Element values: 13 August
Type: [Ljava.lang.Object;  No. of elements: 3                (4)
Element values: 13 August 2009
Type: [Ljava.lang.Object;  No. of elements: 3                (6)
Element values: 13 August 2009
Type: [Ljava.lang.Object;  No. of elements: 1                (7)
Element values: [Ljava.lang.Object;@1eed786
Type: [Ljava.lang.Object;  No. of elements: 1                (8)
Element values: [Ljava.lang.Object;@1eed786
Type: [Ljava.lang.String;  No. of elements: 6                (9)
Element values: To arg or not to arg
Type: [Ljava.lang.Object;  No. of elements: 1                (10)
Element values: [Ljava.lang.String;@187aeca
Type: [Ljava.lang.String;  No. of elements: 6                (11)
Element values: To arg or not to arg