7 Flow Control

As with any language, Java defines a set of expressions and rules. The programmer uses the expressions and follows the rules to define the sequence of steps to be performed by the program. In this chapter, I’ll examine the way a Java program manages flow control.

Java borrows heavily from C++, which has borrowed heavily from C. Nowhere is this relationship more evident than in Java’s syntactic definitions, especially the program flow control operators.

CODE BLOCK

A block of code is a set of Java statements grouped by curly braces {}. Blocks can also have other blocks nested inside them.

     {this is a statement in a block of code;
      this is another statement in the same block of code;

           {this is a statement in a block nested in the first;
            this is another statement in a nested block;
            }
// This is the end of the nested code block.

     }
// This is the end of the first code block.

COBOL organizes statements through the use of verbs such as if, else, and end...if. The end-of-sentence character (period) also defines the end of any conditional group of statements. In Java, multiple statements that are executed as a result of some flow control expression (such as if and else) must be grouped into a code block using the braces.

However, the addition of another statement to the code block would require that these two statements be grouped with braces. Therefore, it is good practice to place braces around single-statement code blocks, especially if they are the result of some conditional expression.

By convention, the statements in a code block are indented to the same column. This helps to visually organize the statement groups and improves readability. (Sun defines a set of code conventions at http://java.sun.com/docs/codeconv/html/Code-ConvTOC.doc.html.)

In addition, it is good programming practice to build your code blocks (that is, create the if and else sections, including the braces) before adding the statements inside the code block. This allows you to concentrate on the two steps independently and, therefore, reduces the number of compile errors and runtime bugs created by syntax mistakes.

THE IF STATEMENT

Structure: if (condition) { block }
    else {block} ;

This basic control flow statement will evaluate condition, and if true, the first block of code will execute; if false, the second block will be performed. As is the case in COBOL, the second code block (the else condition) is optional.

if (inputMsg.equals ("Any Text")) {
     ...
}
else {
     ...
}

condition can be any Java expression that returns a boolean. The result of that expression is evaluated.

if (myErrorMsg.msgText.equals ("Any Text")) {
     ...
}

condition must be a boolean expression, that is, one that evaluates to either true or false. In the example, the equals() method returns a true or false boolean, so it is a good candidate for inclusion in an if statement.

Image

The expression myErrorMsg.msgText.equals contains two member operators (that is, two periods). This implies that two members (a data member and a method member in this case) will be accessed.

Evaluate the expression this way: The data member msgText in the object myErrorMsg is a String variable. As a result, it contains a method named equals(). This method accepts a String parameter and returns a boolean result of true if the passed String parameter contains the same text as is contained in this String. The boolean result is evaluated by the if operator.

Multiple expressions can be grouped, and the result of such grouping will be evaluated. The logical operators AND (&&), OR(||), and NOT (!) group boolean expressions in the same way their text equivalents do in COBOL. For example, this statement:

if (myErrorMsg.msgText.equals ("Any Text")  ||
   (myErrorMsg.msgText.equals ("Some Text")) {
     ...               // The IF code block
}

will perform the code block if myErrorMsg contains either "Any Text" or "Some Text." The following is a syntactically equivalent COBOL statement:

IF (MY-ERROR-MSG = "Any Text") OR
   (MY-ERROR-MSG = "Some Text")
...                  // The IF code block
END-IF.

Java will “short-circuit,” or stop evaluating condition, as soon as an unambiguous result is available. Therefore, it is possible that some of the expressions in condition will not be executed. In the example, if myErrorMsg contains "Any Text", the equals() method will be performed only once.

The order of evaluation of expressions can be controlled using parentheses (). Just as in COBOL, you should liberally use them to clarify your intentions, even if the compiler does figure it out correctly.

     if (myErrorMsg.msgText.equals ("Any Text") &&
          myErrorMsg.msgSize == 8 ||
          myErrorMsg.msgText.equals ("Some Text") {
// This code block will be performed only when the text equals "Any Text"
// since the length of "Some Text" is 9 (that is, it contains 9
// characters).
         ...
}

But the intentions of the developer are unclear, or perhaps there is a bug. The proper code is more likely to be as follows:

     if ((myErrorMsg.msgText.equals ("Any Text") &&
           myErrorMsg.msgSize == 8) ||
          myErrorMsg.msgText.equals ("Some Text")) {
// This code block will be performed for both "Any Text" and "Some Text."
          ...
}

This is a good time to talk some more about local variable scope. Local variables are variables that have been defined somewhere in a Java code block:

     if (inputMsg.equals ("Any Text")) {
         ...
// This next statement defines a local variable inputMsgSize and sets it to
// msgSize.
    int inputMsgSize = myErrorMsg.msgSize;
    ...
  }
else {
    ...
}

The variable inputMsgSize is valid only in the code block in which it has been defined. In the example, that would be the first code block (that is, the statements executed if inputMsg is equal to "Any Text"). Note that these statements are bounded by the first pair of braces.

This variable is also valid in any inner code blocks. Therefore, a statement that sets inputMsgSize to –1 in an inner code block is valid:

     if (inputMsg.equals ("Any Text")) {
         ...
// The next statement defines a local variable inputMsgSize and sets it to
// msgSize.
         int inputMsgSize = myErrorMsg.msgSize;
         if (inputMsgSize == 0) {
// The next statement resets the local variable inputMsgSize.
             inputMsgSize = -1;
             ...
            }
         ...
     }
    else {

Outside the braces for the first code block (for example, in the else condition code block), the variable inputMsg is not valid, and references to it would cause a compile time error:

     if (inputMsg.equals ("Any Text")) {
     ...
// The next statement defines a local variable inputMsgSize and sets it to
// msgSize.
          int inputMsgSize = myErrorMsg.msgSize;
          ...
     }
     else {
// The next statement refers to inputMsgSize outside its code block
// and would cause a compile time error.
          if (inputMsgSize == 0) {
          }
          ...
     }
//  The next statement refers to inputMsgSize outside the whole if statement
//  and would be invalid as well.
     if (inputMsgSize == 0) {
     ...
     }

Note that local variables cannot be defined inside a code block and later evaluated outside it. This sort of result state variable must be defined outside the code block if it is to be used outside the code block.

If an inner code block were to attempt to define a new local variable with the same name as an existing variable (inputMsgSize, in the example), then the Java compiler would detect that as a name collision and report an error. This is, by the way, a departure from some C and C++ compilers, where local variables can be named the same as existing variables—a source of more than a few bugs.

Finally, a local variable named inputMsgSize could be defined in some other code block and used inside that code block. This often happens with temporary variables and counters, such as the commonly used x. Java’s tight scoping rules should allow the compiler to catch most instances of inappropriate use (and reuse) of local variables.

THE WHILE STATEMENT

Structure:  while (condition) { block } ;

This basic loop control statement will evaluate condition and, if true, will perform the block of code. Let’s hope that some statement in the block of code will eventually cause condition to not be true, or else you would have an endless loop!

// Assume errorMsgs is an array of ErrorMsg objects that has previously
//  been created
// ARRAY_SIZE is the maximum number of elements allowed in ErrorMsg.
 //  Examine each ErrorMsg object to find the first with a size equal to 0.
//  Define x outside the while code block.
     int x = 0;
     while (x < ARRAY_SIZE) {
           int inputMsgSize = errorMsgs[x].msgSize;
           if (inputMsgSize == 0) {
// Exit the loop immediately. x will point to this element.
                  break;       // The break statement causes the loop to
                               // exit. The statement after the loop's
                               // ending brace will be the next statement
                               // executed.
          }
          x++;         // Increment the loop variable.
          continue;    // Continue the loop. This statement is not required
                       // but is shown here to show how continue might be
                       // used. The continue statement causes the loop to
                       // proceed with the next iteration.
     }
// The loop has completed.
// At this point either you have found an element that contains a 0 size,
//  or you have examined all of the objects in ErrorMsgs. Test x to see if
//  you tested all of the items in ErrorMsgs (that is, is x equal to
//  ARRAY_SIZE?).
     if (x != ARRAY_SIZE) {
// You have an ErrorMsg with a 0 size !
          ...
     }
     else {
// You have none. The array was exhausted
          ...
     }

As in any programming language, Java loops are often a mechanism to manipulate an array of similar items. I discussed arrays in Chapter 4, and I will discuss the more powerful collection processing in detail in Chapter 11, but a word of review about arrays and array processing in Java seems appropriate at this time.

In COBOL, subscripts for arrays (that is, items that OCCUR x TIMES) start with 1. That means the first item in an array is referenced as ITEM (1), and the last item is referenced as ITEM (x).

For example, the statement:

01 MY-ITEMS    PIC X(2) OCCURS 10 TIMES.

defines an array called MY-ITEMS. Each item in the array is 2 bytes, and there are 10 items in the array. Any subscript in the range of 1 through 10 is valid:

IF (MY-ITEMS(1) = "AA")
    OR (MY-ITEMS(10) = "XX")

However, subscripts outside this range are not valid and will generate either a compile error or a runtime error. Both of these statements will generate an error:

IF (MY-ITEMS(0) = "AA")
    OR (MY-ITEMS(11) = "XX")

In contrast, Java follows the conventions of C and defines the first item in an array as item 0. Therefore, many loops in Java start with a loop variable equal to 0 and end when that variable is equal to the number of items in the array. This means that item[1] is actually the second item in the array, and item[ARRAY_SIZE] is not a valid reference. Instead of the parentheses subscript identifiers that COBOL uses, subscripts in Java are identified with brackets [ ].

THE DO...WHILE STATEMENT

Structure:    do { block } while (condition) ;

This loop control statement is very similar to the basic while statement, except that block will always be executed at least once. condition is evaluated after the code block is performed, and if true, the code block is reiterated.

// Assume errorMsgs is an array of ErrorMsg objects that has previously
// been created.
// ARRAY_SIZE is the maximum number of elements allowed in ErrorMsg.
// Examine each ErrorMsg object to find the first with a size equal to 0.
// Define x and inputMsgSize outside the while code block.
     int inputMsgSize = 0;
     int x = 0;

     do {
           inputMsgSize = errorMsgs[x].msgSize;
           if (inputMsgSize == 0) {
// Exit the loop immediately. x will point to this element.
               break;
     }
     else {
          x++;
     }
}
while (x < ARRAY_SIZE);

In this example, inputMsgSize will be set at least once. Since it has been defined outside the do...while loop, it will be valid outside the loop and will always contain some value as set in the loop.

THE FOR STATEMENT

Structure:    for (expression1; (condition); expression2) { block }

This loop control statement is commonly used to support iteration. The first expression is the counter initialization and is performed only at the start of the loop. The condition is tested to determine if the loop should continue, and if true, the code block is performed. The second expression is the expression that changes the counter. It is performed after the code block is completed, but before the next iteration of the loop. The code block will be performed iteratively as long as condition is true. After every iteration of the code block, and if condition is true, the counter increment expression (expression2) will be performed.

     for (int x = 0; x < ARRAY_SIZE ; x++) {
           int inputMsgSize = errorMsgs[x].msgSize;
           if (inputMsgSize == 0) {
// Exit the loop immediately. x will point to this element.
               break;
          }
     }

Note that the sample is not of much use, since both x and inputMsgSize have been defined inside the loop and are, therefore, not available outside it (x was actually implicitly defined in the for statement). If you tried to use either x or inputMsgSize outside the loop, you would create a compiler error.

It would be necessary to define these variables before the loop if you want to use them after it is complete.

// Define x and inputMsgSize outside the while code block.
     int inputMsgSize = 0;
     int x = 0;

     for (x = 0; x < ARRAY_SIZE ; x++) {
           inputMsgSize = errorMsgs[x].msgSize;
           ...   // as before
     }

You may have noticed the similarities between the for loop and the while loop. The for loop is a combination of the most common structures used in a while loop. For example, this is a common while loop structure.

initializationExpressionA;
while (testExpressionB) {
     performCode;
     incrementExpressionC;
}

This is combined in a for loop as follows:

for (initializationExpressionA; testExpressionB;
     incrementExpressionC;) {
     performCode;
}

THE SWITCH STATEMENT

Structure:  switch (expression) {

case (statement1):
        code block1 ;
        break;

case (statement2):
        code block2 ;
        break;

default:
        code block3 ;
        }

This flow control statement is commonly used to perform various functions based on the value in a variable. It is similar to COBOL’s EVALUATE verb. expression is evaluated and then compared for equality to each of the case statements. If expression is equal to the case statement, the code block for that statement is performed.

int inputMsgSize = 0;
int x = 0;

for (x = 0; x < ARRAY_SIZE ; x++) {
      switch ( errorMsgs[x].msgSize)
      {
      case 0:
         errorMsgs[x].setErrorMsg("Default Text");
         break;
     case 1:
     case 2:
     case 3:
        errorMsgs[x].setErrorMsg("Text < 4 chars");
        break;
     default:

     }
}

This statement does have a number of limitations. You can evaluate only certain primitive types (char, byte, short, int) and enumerated types. You cannot use ranges of values, as is possible with Level 88s in COBOL. And there are a few surprising little side effects, depending on whether the break statement is performed.

Normally, each code block is coded with a break in order to exit the switch loop. If a break is not defined, then upon completion of the case code block, the evaluation continues with the next case condition. This may or may not be what you intended, so you should always place breaks in case statements; and if you want evaluation to continue, make sure that you document it.

THE BREAK, CONTINUE STATEMENTS

I’ve introduced the statements break and continue by example rather than with a formal definition, so let’s address these statements. These are statements that manage flow control in all the loop structures (while, for, switch, and so forth). The break statement causes the current loop to exit or complete immediately. The continue statement causes the current loop to be reiterated from its beginning, preserving the current values. Consider the following examples:

     int x;
     int inputMsgSize = 0;
     for (x = 0; x < ARRAY_SIZE ; x++) {
           inputMsgSize = errorMsgs[x].msgSize;
           if (inputMsgSize == 0) {
// Exit the loop immediately. x will point to this element.
               break;
          }
     }
// Evaluate inputMsgSize after the loop.
     if (inputMsgSize == 0) {
           ...
     }

The break statement exits the current for loop at once. Variables modified by the loop will maintain their current values and may be available after the break statement is executed, depending on how the variables were defined. In the example, inputMsgSize will be set to 0 if the break was performed.

In contrast, consider this example:

     for (x = 0; x < ARRAY_SIZE ; x++) {
           int inputMsgSize = errorMsgs[x].msgSize;
           if (inputMsgSize == 0) {
// Continue with the next item in the loop.
               continue;
            }
// Translate the error message using the getTranslation method.
// Note that the String parameter passed to the setErrorMsg() method is the
// result of the getTranslation() method.
          errorMsgs[x].setErrorMsg ((errorMsgs[x].getTranslation()));
       }

The continue statement causes the loop to continue with the next iteration. In the example, error messages with sizes equal to 0 will not be translated. But the loop will continue until all the error messages are examined (and translated, if they contain text).

The break statement and the continue statement affect only the current loop. If loops are nested, and a break is performed, then program flow will continue with the next statement after the current loop. Conversely, continue will cause the current loop to be reiterated.

     for (x = 0; x < ARRAY_SIZE ; x++) {
           int inputMsgSize = errorMsgs[x].msgSize;
           if (inputMsgSize == 0) {
// Find the next item with some text. Then move its text into this
item's text.
             for (y = x + 1; y < ARRAY_SIZE ; y++) {
                 inputMsgSize = errorMsgs[y].msgSize;
                 if (inputMsgSize != 0) {
                    errorMsgs[x].setErrorMsg
                      (errorMsgs[y].getErrorMsg);
                    break;
                 }
               }
           }
// The break statement causes this statement to be processed as the
next statement.
// Translate the error message.
           errorMsgs[x].setErrorMsg ((errorMsgs[x].getTranslation()));
       }

In this example, the break statement causes the inner for loop to be exited. The outer loop will continue.

What happens if no error messages contain text? You would attempt to perform the translate method without any text! Let’s hope the method is robust enough to deal with this condition, but suppose it isn’t? The best way to code for this situation would be to break out of the outer and inner loops as soon as you discover that there is no text to translate.

Java provides a labeled break statement to help with this requirement. This statement allows the programmer to specify which loop should be exited. It’s as close as Java gets to a goto statement. goto is not a valid Java word, but it is reserved (that is, it is not valid as a user-defined name).

Without starting any religious arguments, it is fair to say that there are situations where an explicit statement to exit a loop is a superior construct than complex and hard-to-maintain if...break and if...continue statements. Once the decision is made to exit a particular code block, it is better if the code clearly states that intention.

You can extend the example as follows:

// First, define a label at the beginning of your loop.
Translate_loop:

     for (x = 0; x < ARRAY_SIZE ; x++) {
           int inputMsgSize = errorMsgs[x].msgSize;
           if (inputMsgSize == 0) {
// Find the next item with some text. Then move its text into this
// item's text.
               for (y = x + 1; y < ARRAY_SIZE ; y++) {
                   inputMsgSize = errorMsgs[y].msgSize;
                   if (inputMsgSize != 0) {
                      errorMsgs[x].setErrorMsg
                        (errorMsgs[y].getErrorMsg);
                      break;
                   }
                   else {
                      if (y == (ARRAY_SIZE - 1)) {
// You are on the last item, without finding text. Exit the translate
// loop entirely.
// Do not process any more items.
                              break Translate_loop;
                          }
                       }
                  }
            }
// Translate the error message.
          errorMsgs[x].setErrorMsg
                ((errorMsgs[x].getTranslation()));
  }
// This statement will be processed as the next statement after
// break Translate_loop; is executed.
// It is also performed as the next statement after the loop exits
// normally.
     System.out.println ("The loop has completed");

Table 7.1 summarizes the Java flow control operators.

TABLE 7.1 JAVA FLOW CONTROL OPERATORS

EXERCISES: FLOW CONTROL

Time to visit the example classes again and try out all these new ideas.

  1. Edit the HelloWorld.java source file in your java4cobol directory with a text editor. Remove the lines after the if statement where you tested the value of textMsg.

  2. First, you need to adjust the if code block in order to explore the scope of local variables. After the if statement where you tested the value of textMsg, add these additional bolded Java statements:

    // Experiment with Java statements.
              String testMsg = myErrorMsg.getErrorMsg ();
              if (myErrorMsg.getErrorMsg ().equals (testMsg)) {
    // Define a temporary integer variable.
                int i = 5;
                System.out.println ("testMsg = text in ErrorMsg");
                System.out.println ("i = " + i);
              }
              else {
                   System.out.println ("i = " + i);
              }
              System.out.println ("i = " + i);

    Attempt to compile this class. You should get an error message indicating that the compiler does not know the definition of the variable i. Delete the first println statement, then the other statement. Is either valid? What does this tell you about the scope of local variable i? Can it be accessed outside the code block where it was created (as defined by a pair of matching braces {})? Where would you need to place the definition of i in order to compile the previous Java statements? Go ahead and try it.

    The code samples presented till now have all used the // style of comment identification. You will experiment with other styles of comment identification.

  3. Comment out the lines you just added:

    // Experiment with Java statements.
              String testMsg = myErrorMsg.getErrorMsg ();
              if (myErrorMsg.getErrorMsg ().equals (testMsg)) {
    // Define a temporary integer variable.
                   int i = 5;
                   System.out.println ("testMsg = text in ErrorMsg");
                   System.out.println ("i = " + i);
              }
    /* Comment out the next few lines.
              else {
                   System.out.println ("i = " + i);
              }
              System.out.println ("i = " + i);
    Comment out the next few lines. */

    Recompile this program.

  4. Let’s work with one of the more common Java constructs, the for loop. As mentioned earlier, this is analogous to the PERFORM COBOL verb. Add these lines to the end of your HelloWorld class:

    // Experiment with for and case.
         int even = 0, odd = 0, other = 0, total = 0, i;
         for (i = 0; i < 9; i++) {

              total++;
              switch (i) {

              case 1:
              case 3:
              case 5:
              case 7:
                   odd ++;
                   break;

              case 2:
              case 4:
              case 6:
              case 8:
                   even ++;
                   break;

              default:
                   other ++;
                   break;
              }
          }
          System.out.println ("Odd, Even, Other, and Total = " + odd + ", " +
               even + ", " + other + ", " + total);

    Compile and rerun the HelloWorld application. The output should look like this:

    ...
    Odd, Even, Other, and Total = 4, 4, 1, 9

    Look closely at the for loop definition and the output line. How many times did the for loop execute? What do you think the range of values for i were as the loop executed?

    Remove the break statement in each of the case sections. What happens? Why did this happen? Hint: Refer to the introductory discussion on the case statement.

  5. Let’s test how the continue statement works in a for loop. Add these bolded lines to your HelloWorld class:

    // Experiment with for and case.
         int even = 0, odd = 0, other = 0, total = 0, i;
         for (i = 0; i < 9; i++) {

              total++;
              if (i > -1 ) {
                   continue;
              }
              switch (i) {

    Compile and rerun the HelloWorld application. The output should look like this:

    ...
    Odd, Even, Other, and Total = 0, 0, 0, 9

    Look closely at the for loop definition and the output line. How many times did the for loop execute? Why do you think that no variables except total were incremented?

  6. Let’s create a for loop inside the original one. You will also observe the effect of a break statement in a for loop. Insert comment markers for the italicized lines, and add these bolded lines to your HelloWorld class:

    // Experiment with for and case.
         int even = 0, odd = 0, other = 0, total = 0, i;
         for (i = 0; i < 9; i++) {

              total++;
    //       if (i > -1 ) {
    //            continue;
    //       }
             switch (i) {

              case 1:
              case 3:
              case 5:
              case 7:
                  odd ++;
                  for (int i2 = 0; i2 < 10; i2++) {
                  odd++;
                  break;
                  }
                  break;
             case 2:
             case 4:
             case 6:
             case 8:
                  even ++;
                  break;
             default:
                  other ++;
                  break;
             }
        }
        System.out.println ("Odd, Even, Other, and Total = " + odd + ", " +
             even + ", " + other + ", " + total);

    Compile and rerun the HelloWorld application. The output should look like this:

    ...
    Odd, Even, Other, and Total = 8, 4, 1, 9

    Look closely at the for loop definition and the output line. How many times did the outer for loop execute? Why do you think that the variable named odd was only incremented once for each execution of the inner loop, even though the inner loop definition says it should be performed 10 times?

  7. For the last exercise, you’ll define a labeled break. This statement allows you to go to the end of a code block from inside the code block. Add these bolded lines to your HelloWorld class:

    // Experiment with for and case.
         int even = 0, odd = 0, other = 0, total = 0, i;
    outerloop:
         for (i = 0; i < 9; i++) {

              total++;
              switch (i) {
              case 1:
              case 3:
              case 5:
              case 7:
                   odd ++;
                   for (int i2 = 0; i2 < 10; i2++) {
                        odd++;
                        break outerloop;
                   }
                   break;
              case 2:
              case 4:
              case 6:
              case 8:
                   even ++;
                   break;

              default:
                   other ++;
                   break;
              }
         }
         System.out.println ("Odd, Even, Other, and Total = " + odd + ", " +
              even + ", " + other + ", " + total);

    Compile and rerun the HelloWorld application. The output should look like this:

    ...
    Odd, Even, Other, and Total = 2, 0, 1, 2

    Look closely at the labeled for loop definition and the output line. How many times did the outer for loop execute this time? Why do you think that the variable named Even was never incremented in the loop?

REVIEWING THE EXERCISES

Image

Let’s review the samples you’ve created. Try to relate the sample source statements to the result (that is, the output) each statement creates. If necessary, rerun the samples, or look at the complete source code for this exercise on the CD-ROM. Feel free to experiment by yourself.

  • The switch and case statements are similar to COBOL’s EVALUATE verb.

  • Java’s for loop is similar to COBOL’s PERFORM UNTIL construct. The continue statement causes the current iteration of this loop to terminate and the next iteration to start. The break statement causes the current code block to be exited immediately.

  • Finally, you constructed a labeled for loop. After the loop was performed only two times, you exited the labeled loop abruptly, causing the statement after the loop to be executed. In fact, you exited the outer loop from inside an inner loop.

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

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