The order of the material in the book makes it hard to present static methods before presenting classes. Since that topic is to be covered on Monday November 6th, I am asking you to read this handout in preparation for the class on November 3rd.

Executive Summary

Details

Mathematical Functions

Functions are essential to both mathematics and computer science. Consider the trigonometric function sine(degree). The first question to ask is "what is the value expected by the function". In this case, the sine function is expecting a double value. Now, as you may also be aware, sometimes we count angles by degrees (i.e., a right angle is 90 degrees); sometimes by radians (i.e., a right angle is p/2 ). Mathematically, we would define this function as:

sine(degree) = sin (2*p*degree/360)

that is, we would convert the degree into radians, before computing the trigonometric sin function.

If we want to define a Java method to represent this mathematical function it would look like this:

/**
 * Compute the sine of an angle of the given degree.
 *
 * @param degree is in the range 0 <= degree <= 360
 * @return computation of the trigonometric sine function
 */
public static double sine (int degree) {
  // convert degrees into Radians, and return result.
  double rad = degree*2*Math.PI/360.0;
  double val = Math.sin(rad);

  return val;
}

Reading left to right, this says "define a new public method returning a double whose name is sine, and which takes a single argument whose value is a int". The body of the method is found between the "{" and "}" as you would expect. Because this method is defined as returning a value of type double, the method body must include a return statement that properly returns a value of the declared type. When a method uses if statements as part of the control flow, there may be more than one return statements defined in the method. However, only one will be executed at a time. Thus a method has a well-defined entry point (the first statement in the method will be executed when the method is invoked) and may have several well-defined exit points (each return statement).

The documentation for the method appears just before it, using the "/** ... */" format that is key to the way in which Java programs are documented. The first line of text is meant to be a single sentence (ending in a period) that describes the purpose of the method. Each @param tag describes information about the parameters to the function. Each parameter appears in the parameter list of the method, enclosed by parentheses. When multiple parameters are present they are separated by commas. The @return tag describes what is returned by the method (if anything).

A method may be defined to return no computation, in which case it (perhaps) just outputs some value to the console, or performs a task without needing to respond with an output. Note how the main methods we have been using all along declare their return type to be void; for example:

/**
 * Just say Hello, World.
 *
 */
public static void hello () {
  System.out.println ("Hello, World.");
}

Note that even when a method has no arguments (as above) you must still include the "()" in its declaration. Note also that a void method cannot return a value, thus any attempt to use the return statement within a void method is a syntax error. Methods cannot exist outside of a Java class, so we must always define them within one. We show how to do this next.

Adding a Static Method to a class

In our example programs to date, there has been a single method declaration, main, declared as:

package somePackage;

public class Example {

  public
static void main (String []args) {
     // Body here
  }

}

This method is special (as I mentioned in class) and is the "entry point" into the computation. That is, when you "Execute" the program, this is the method whose statements are executed. Each method in the class (FOR NOW: but stay tuned for Tuesday November 7th) is defined using the following template:

/**
 * One-line description of method
 * @param    small-description-of-parameters
 * @return    description-of-what-is-returned
 */
public static return-type method-name (parameter-list) {
  Method-body 

  return information-to-return
}

So, let's add the hello() method defined above to our Example class:

package somePackage;

public class
Example {

  /**
   * Just say Hello, World.
   */
   public static void hello () {
     System.out.println ("Hello, World.");
   }

   public static void main (String []args) {
     // invoke the static method defined within this class.
     // Name methods so you understand what is happening.
     hello();
  
     // Think about what you have just done. In some respect, you have "extended"
     // the Java language to have a new statement that outputs "Hello, World" whenever
     // you want. No matter how trivial this sounds, you have just witnessed the true
     // power of programming.
  }
}

The control flow of the program starts in main and can now branch out to other methods as necessary. The primary challenge is to manage complexity by the appropriate definition of methods.

In general, programs rapidly become too complex to place all statements within a single method, so we now can show how to add a new method to a class. There are many good reasons to do so. Here is one: Common code that is used more than once can be placed in a separate method to be reused as often as needed. For example, we have seen numerous examples where the user typed in a sequence of int values to be stored in an array. This is quite useful functionality that we have identified. Let's pull it out and make a separate function. Here is what it would look like:

/**
 * Read using the given Scanner object an array of n int values, as determined by the
 * user.
 *
 * @param    sc is object from which to read input.
 * @return    array of int values
 */
public static int[] readArray (Scanner sc) {
  System.out.println ("How many numbers to enter?");
  int num = sc.nextInt();

  int []vals = new int[num];

  /** Load up values. */
  System.out.println ("Enter " + num + " values.");
  for (int i = 0; i < num; i++) {
     vals[i] = sc.nextInt();
  }

  return vals; 
}

Let's place this method in a class, and invoke it. Note how clean the main method looks now. We are truly creating code that can be easier understood because it is being broken into meaningful chunks.

package nov03;

import java.util.Scanner;

public class SampleQuestion {
 /**
  * Read using the given Scanner object an array of n int values, as
  * determined by the user.
  *
  * @param sc is object from which to read input.
  * @return array of int values
  */
  public static int[] readArray (Scanner sc) {
    System.out.println("How many numbers to enter?");
    int num = sc.nextInt();
   
    int[] vals = new int[num];
   
    /** Load up values. */
    System.out.println("Enter " + num + " values.");
    for (int i = 0; i < num; i++) {
      vals[i] = sc.nextInt();
    }
    return vals;
  }
 
  /** output. */
  public static void outputLessThan50 (int[] ar) {
    int ct = 0;
    for (int i = 0; i < ar.length; i++) {
      if (i < 50) {
        ct++;
      }
    }
 
    System.out.println (ct + " vals were less than 50.");
  }
 
 
  /** You must fill in the helper methods to make this example work. */
  public static void main(String[] args) {
    Scanner sc = new Scanner (System.in);
    int nums[] = readArray(sc);
    outputLessThan50(nums);
  }
}

Discussion of a parameter

A method receives its input from the parameters that it defines. For example, to compute the volume of a cylinder, you need radius radius and height height. To write this method, you need to ask some questions: What type should be used to represent the radius? What type should be used to represent the height? What type should be used to represent the final calculation? in all cases, the answer might be double, so the method would look like:

/**
 * Compute volume of a cylinder with radius r and height h.
 *
 * @param     radius is the radius of the cylinder
 * @param     height is the height of the cylinder
 * @return     calculate volume
 
*/
 public static double cylinderVolume (double radius, double height) {
   double val = Math.PI * radius * radius * height;
   return val;

 }

 public static void main (String []args) {
    double r = 5.0;
    int h = 2.2;
    double vol = cylinderVolume (r, h);

    System.out.println ("Volume is:" + vol);

  
}


The parameters for a method define its input. Read from left to right, these values are all going to be set prior to the execution of the first statement in the method body. Within the method body, you can use these variables as you would any other variable that was defined in the method body.

Discussion of an argument

You have seen arguments in several examples so far. In the statement "double d = Math.sqrt(5)", the method sqrt takes a single argument 5. Thus, when you invoke a method, you have the opportunity to pass in any number of arguments, defined by any number of types. The arguments are read from left to right and are ultimately matched (left-to-right) against the parameters that are defined with the method.

Given the cylinderVolume method just defined, the method is "invoked' by the statement "cylinderVolume (r,h)" above. The next method in the program executed is the first statement within the method declaration for cylinderVolume. To make this happen, the VALUE of the variables r and h are copied into the parameters radius and height. Thus, when the first statement of cylinderVolume executes, the value of radius is 5.0 and the value of height is 2.2. The cylinderVolume method executes to completion, at which point it returns the calculated value (for the curious: 172.78759594743863) which is then stored in the variable vol. The control flow of the statement "double vol = cylinderVolume (r,h)", then, is "compute the value of the method cylinderVolume with arguments r and h; once that has completed (and we have every expectation that this calculation will not require an infinite amount of time), store the computed result in the vol variable".

Discussion of method body

You can consider the body of a method as a "mini computation". It has all the rights and privileges of the programs we have written so far. It can output information to the console by means of System.out.println() statements. If a Scanner is available, then it can read input from the keyboard. Most importantly, it can define local variables that are known only by that method. Once the method completes its execution, the local variables are gone and can't be accessed again. A method can instantiate an array by means of the new operator, as we have seen the main method do. This array will be reclaimed if it is not returned. A method can only return information to the one who called it (the invoker) by means of a return statement. A method can only return a single value.

Discussion of a return type

If a method chooses to return nothing, then it must be declared with the void return type. Note that void can only be used when defining the method. It is not a type that can be associated with a variable, like:

void var;    // INVALID

Once a method is declared to have a return type, then you must ensure that each possible exit point from the method is terminated by a return statement. What does this mean?

// NOTE: this won't compile
public static int badMethod (int x) {
   if (x < 10) {
      return 99;
   }
}    

Consider the badMethod proposed above. When invoked, its x variable is set to some int value. If this value is less than 10, then the method returns 99. However, there is no defined return statement to cover the case when x is not less than 10. To properly fix this, you need a return statement at the end of the method. The following would work:

// compiles, although meaningless
public static int fixedMethod (int x) {
   if (x < 10) {
      return 99;
   }

   return 12;
}    

Discussion of a Call Stack

When a method m invokes another method n, then the computation defined by the method body of m must pause until n completes. If method n then calls another method p, then n must similarly pause (m is still paused) until p completes its execution. To capture the behavior just defined, computer scientists developed the notion of a "Call Stack". You can imagine that when method p begins its execution, there is a stack with values [n m] on it. That is, if you were to remove (pop) from the stack, then n would be returned, leaving the stack with value [m]. So, when a method is invoked, it is "pushed" onto the call stack. When it completes, it is "popped" off the call stack. In the Java Programs we are constructing to date, the "bottom" of the call stack is going to be the main method.

END NOTES

If any errors/omissions to the above are discovered before Friday, I'll place errata here.