Methods: A Review


Objectives

 

The goal of this module is to review some key ideas in programming:

For this purpose, we will use some contrived but simplified examples to drive home important points.

Most of our focus will be on methods.


Variables

 

Consider this example:


public class VariableExample {

    public static void main (String[] argv)
    {
	int x = 5;

	double myFavoriteRealNumber = 3.141;

	char c;

	c = 'a';

	boolean thisIsFun = false;

	int[] odd = {1, 3, 5, 7};

        x = odd[0] + odd[3];

	c = 'b';
    }

}
    

Variables have three aspects to them:

 

About variables:


Visualization of execution

 

First, let's review mental execution and develop some useful visualizations:

  • Consider this program
    
    public class SillyProgram {
    
        public static void main (String[] argv)
        {
    	int x = 1;
    	int y;
    	x = x + 1;
    	y = x*x;
    	System.out.println (y);
        }
    
    }
        

  • When you see such a program, you should say ...
    • Execution starts with the first line in main.
    • Here, an int variable x is declared and assigned the value 1.
    • Then, the int variable y is declared, but not assigned a value ... etc.

  • Thus, execution proceeds sequentially in order of the appearance of statements.
 

So far, so good. Next, let's look at:


public class ForLoopExample {

    public static void main (String[] argv)
    {
	int x = 0;
	for (int i=1; i<=10; i++) {
	    x = x + i;
	}
	System.out.println (x);
    }

}
    

There are four levels at which to "read" or understand a for-loop:

  • First, at the highest level, a for-loop is merely a block of code that repeats a number of times:

  • At the next level, you should say to yourself, "for i starting at 1 up through 10 ...(something)"

  • At the third level, you can try and understand what the loop is trying to do:

  • Finally, at the most detailed level, you trace through the loop, watching how every affected variable changes its value.
           => Here, only x and i change.
    
                                     i             x
      0.  Before loop starts                       0
      1.  End of 1st iteration       1             1
      2.  2nd                        2             3
      3.  3rd                        3             6
      4.  4th                        4             10
      5.  5th                        5             15
      6.  6th                        6             21
      7.  7th                        7             28
      8.  8th                        8             36
      9.  9th                        9             45
      10. last iteration            10             55
        
 

Now let's look at a conditional:

  • First, at the outermost level, notice that exactly one of three blocks will execute:

  • For this data, the execution turns out to be:

    1. First, the Boolean expression (x < y) is evaluated.
             => This fails and goes to the else, which has an if.
    2. Then, (x > y) is evaluated
             => This turns out to be true, and so, one enters the block inside.
    3. It so happens that the first statement inside is an if.
             => The complex conditino turns out to be true.
    4. Then, (c == 'a') is evaluated.
             => Which turns out to be false.
    5. The else block is entered. This itself has an if inside.
             => The condition (c == 'b') turns out to be true.

  • Now, consider what happens after the println executes:

    1. The innermost if has no corresponding else
             => Execution continues after the if-block (just past the closing brace).
             => There's nothing there, so we come out of the else corresponding to (c == 'a').
    2. There's nothing there, so we're done with the if-block with the complex condition.
             => This means we jump past the corresponding else.
    3. There is nothing to execute here, so we are done with the if-else with the complex condition.
             => We jump out of the code block corresponding to (x > y).
    4. Finally, we jump past the outermost if-elseif-else block.
 

In-Class Exercise 1: Add println's at places numbered 6-9 above. Make these print the numbers 6, 7, 8, and 9. Then, add println's just after the conditions corresponding to 1, 2, and 3 (to print these numbers). Did the output correspond to the flow of execution?


Parameterless methods

 

Consider this simple example:

  • First, remember that execution always starts at the first line in main.

  • Recall that a method is defined once, but can be invoked several times (as above).

  • Let's trace execution up through the first invocation:

    1. Execution up through just before the method.
    2. Then the method call.
    3. Inside the method.
    4. When the method completes execution, we return to the line just after the method call.

  • The second call is similar:

  • Note: methods can be declared before invocation.

  • Methods can be invoked any number of times, in various places:

 

In-Class Exercise 2: Without executing the above program, what is the output?
 

Methods can call other methods:

 

In-Class Exercise 3: What is the output of the above program? Where is System.out.print called for the 5th time?


Methods with parameters

 

Here's an example of defining and invoking a method with a parameter:

  • Notice that there is a single int parameter taken in:

    Once the method starts executing there will be a value in n.

  • The method call can use a constant directly

    or can use the value from an int variable

  • Method parameters are like variables, you can modify their values:

 

In-Class Exercise 4: What is the output of the above program?


The return statement

 

First, let's look at methods that don't return a value.

  • Remember this: every method returns.
           => It's just that a return at the end is unnecessary.

  • One could, even if it's redundant, write:

  • Notice that no value is returned.
           => This is why the return type is void

  • A method can have any number of return statements:

 

In-Class Exercise 5: Without executing it, what is the output of the following program?


public class ReturnExercise {

    public static void main (String[] argv)
    {
	printXsAndYs (3, 4);
	printXsAndYs (4, 1);
	printXsAndYs (-1, 1);
	printXsAndYs (4, -1);
    }

    static void printXsAndYs (int m, int n)
    {
	System.out.print ("start ");
	if (m < 0) {
	    // 1.
	    return;
	}
	else if (m > 10) {
	    // 2.
	    return;
	}
	for (int i=0; i<m; i++) {
	    System.out.print ("X");
	    printYs (n - i);
	}
	System.out.println (" end.");
    }

    static void printYs (int k)
    {
	if (k < 0) {
	    // 3.
	    return;
	}
	else if (k > 10) {
	    // 4.
	    return;
	}
	for (int i=0; i<k; i++) {
	    System.out.print ("Y");
	}
    }

}
    
Write some println's where the numbered comments are to help you see how the program executes.

Methods that return a value

 

Here's a simple method that returns an int:

  • Notice that the return type is now declared as an int:

  • After execution, the return statement causes the value of n to be assigned into q

  • For purposes of understanding, it might help to visualize this as:

 

A method call that returns something can be used in expressions:

  • Here, start by looking at the outermost call:

  • Then, say to yourself "Oh, the parameter itself is a method call, which will need to complete to get the value".

  • But that is itself a method call:

 

In-Class Exercise 6: Insert a println into the incr method to see the order of execution.
 

In-Class Exercise 7: Without execution, evaluate the output of the code below:


    int p = 3;
    int q = incr( incr(p) + incr(2) );
    int r = incr( incr(q) / p );
    
 

One can have multiple return statements in a method. Only one will actually execute in a particular invocation:

 

In-Class Exercise 8: Which return statement gets executed above? Add one println before each return in factorial. Then, try this code in main:


    int p = 3;
    int q = factorial (factorial(p)) * factorial(p-3) + factorial (-3);
    
 

In-Class Exercise 9: The program below has three errors. Can you see them without compiling the program? Fix the errors.


Scope

 

Consider this program:

  • First, note that the there are two different p and q variables:

  • The scope of a variable is the body of code that's allowed to access (or use) the variable.

  • For example:

  • Likewise ...


Arrays are different

 

Consider this program:

  • Here, the output is:
    
        3
        [2, 3, 4]
        
    Thus, the variable p is not affected by the call to incr whereas the array A certainly is.

  • Arrays are fundamentally different from basic types.

  • When an array is passed as parameter, it is passed as a so-called reference
           => This means the invoked method has access to the array, and can modify its contents.

  • This stays true no matter how the variables are named:

  • An aside: methods that have different signatures can use the same name:


Why we use methods

 

Methods are very useful for four different reasons:

  1. Code written in a method can be re-used.
    For example, compare

    with

  2. The second big reason is composability, as this example shows:

  3. A long program broken up into methods will make the program more readable and therefore more easily understood.

  4. The biggest reason, perhaps, is that it has become one of two important ways by which multiple programmers use each others' code.

    Example: you have used methods in DrawTool, and Java library methods like Math.random().

 

How do you know when to create methods vs. writing long code?

  • There are no rules. This comes by practice.

  • Generally, tiny computations like increment don't need methods.

  • Any significant computation that is likely to be re-used should probably be in a method.

  • Use methods when breaking things into methods greatly improves readability.



© 2011, Rahul Simha