Notes for the week of April 11-15

After considering the issues the grader raised about calling methods from the constructor, and our discussion in class today, I'm going to make the following recommendation.

Given that the order of execution in a Java object class is always the following:

  1. The constructor gets called
  2. Field variables are initialized

it is ok to call methods from the constructor, as long as you clearly understand that no field variables will have yet been initialized.

For example, one way to make sure that the field arrays X[] and Y[] in Triangle.java are allocated before you assign values to them is to use the following implementation for setTriangleVertices:

   public void setTriangleVertices() {
      if (X == null)
         X = new int[3];

      if (Y == null)
         Y = new int[3];

      X[0] = x;
      X[1] = x + width / 2;
      X[2] = x + width;
      Y[0] = y + height;
      Y[1] = y;
      Y[2] = y + height;
   }
The "BOOLEAN ? EXPR : EXPR" construct:

On Tuesday we talked about an alternate way to do conditional execution. Recall that the if / else statement allows you to execute either one set of statements or else another:

   if (BOOLEAN) {
      SOME STATEMENTS
   }
   else {
      SOME OTHER STATEMENTS
   }
You can also create conditional execution of expressions rather than entire statements, as follows:
   BOOLEAN ? EXPRESSION1 : EXPRESSION2

If the BOOLEAN expression is true, then only EXPRESSION1 is executed. Otherwise, only EXPRESSION2 is executed. For exaomple:

   int n = x < 100 ? someVariable + 10 : someVariable + 20;
will assign a value of someVariable + 10 to n if x is less than 100. Otherwise it will assign a value of someVariable + 20 to n.

Conditional expresisons have lower precedence than arithmetic operations, so if you want to use them within an arithmetic expression, be sure to put parentheses around them, as in the following example:

   int n = someVariable + (x < 100 ? 10 : 20);
Note that this example does exactly the same thing that the previous example did, but is more compact and easier to read. Note also the parentheses, which ensure that the conditional expression willhappen before the final + operation.

The switch/case statement:

On Tuesday we also went over the switch/case statement, which is a way to choose between a set of choices of statements to execute. The general form is:

   switch (INTEGER_EXPRESSION) {
   case INTEGER_CONSTANT:
      STATEMENTS
   case INTEGER_CONSTANT:
      STATEMENTS
   ...
   }
There can also be an optional default: case, which gets executed if none of the cases are true.

When you have a set of choices, the switch/case statement is both more efficient and easier to read than a chain of if/else statements.

By default, each case "drops through" to the next case. This can be useful. For example, the following code computes the factorials of each of the first 10 positive integers:

   int factorial = 1;
   switch (n) {
   case 10: factorial *= 10;
   case  9: factorial *=  9;
   case  8: factorial *=  8;
   case  7: factorial *=  7;
   case  6: factorial *=  6;
   case  5: factorial *=  5;
   case  4: factorial *=  4;
   case  3: factorial *=  3;
   case  2: factorial *=  2;
   }

But usually you don't want to drop through to the next case. In those situations, you should use the break statement, which immediately breaks execution out of the switch/case construct. Here is an example:`

   String statement = "";
   switch (n) {
   case 1: statement = "My name is " + name; break;
   case 2: statement = "Your name is " + name; break;
   case 3: statement = "Is " + name + " your name?"; break;
   }
As one of the students pointed out, you can also break out of a case with a return statement. Here is an example:
   public int powers(int n, int p) {
      switch (p) {
      case 1: return n;
      case 2: return n * n;
      case 3: return n * n * n;
      case 4: return n * n * n * n;
      case 5: return n * n * n * n * n;
      default: return 1;
      }
   }
Note the use of the default statement, which guarantees that the switch statement will return some value no matter what is the value of n.

Recursion:

We also talked about recursive methods -- methods that call themselves. For example, one way to compute the Fibonacci series is:

   int fibonacci(int n) {
      if (n <= 1)
         return 1;
      else fibonacci(n - 1) + fibonacci(n - 2);
   }
Unfortunately the above implementation is very inefficient, since the computation cost becomes exponentially large as n increases.

A more efficient recurive algorithm is as follows:

   int fibonacci(int n) {
      return fibonacci(n, 0, 1);
   }

   int fibonacci(int n, int a, int b) {
      if (n == 0)
         return a;
      else
         return fibonacci(n - 1, b, a + b);
   }

Packages:

We're not going to do anything with packages quite yet, but I'm putting the notes on-line here so that you can get a chance to become familiar with the concept.

Packages are a way that Java lets you organize object classes, using your file system's own directory structure. Rather than put java files into your current folder, you can place a set of related Java files into a subfolder. In each of those Java files you need to add a line that declares that it is a member of that package. For example, file "shape/Shape.java" might contain:

package shape;

public class Shape
{
    ...
}
   
A program that uses the Shape class should put an import statement at the top:
import shape.*;

public class myProgramThatUsesShape
{
   ...
   Shape shape = new Shape(...);
   ...
}

Inner classes:

In class on Thursday we briefly covered inner classes. I'm going to be going over this concept in greater detail next week, so I'm going to hold off putting the notes about this on-line until we can cover it in greater detail.

Homework:

There will be no homework yet. I'm going to give people a chance to catch up and read over this week's class notes, so that you can be more prepared for the assignment that I will give next Tuesday.