Incremental Java
Parameters and Method Definitions

Next Step

Last time, we talked about filling out the rest of the details of writing a class definition.

From the object user's perspective, they know:

In addition, to complete a class definition, a class designer must also come up with This information is usually hidden from the object user. This allows the class designer to make future changes, if needed, without affecting the user.

Last time, we talked about adding instance variables to the class definition. This is what it looked like:

// (Incomplete) class definition 
public class Rectangle 
{
   // Instance variables
   private int height, width ;  

   // Constructors
   public Rectangle() ;
   public Rectangle( int initWidth, int initHeight ) ;

   // Method Signatures
   public void setWidth( int newWidth ) ;
   public void setHeight( int newHeight ) ;
   public int getWidth() ;
   public int getHeight() ;
   public int getPerimeter() ;
   public int getArea() ;
   public boolean isSquare() ;
}
The instance variables are right at the beginning.

Filling a Method Definition

Class definitions have more information than just the method signature. There's also Java code for the definition is.

Let's write the method definition for setWidth().

// Method definition for getWidth()
public void setWidth( int newWidth ) 
{  // Start of method body
   width = newWidth ;        // Method body
}  // End of method body

Scope

A method definition consists of a method header (which is basically the method signature, but doesn't end in a semicolon).

Then, there's the method body, which consists of an open brace, some code, and a close brace. The method body consists of Java code.

This method body is called a scope. I can't easily define scope for now, but it indicates a region where variables are valid.

Within this scope, you have access to the following variables:

In this scope, you can not access the local variables or parameters of another method body. You can make a method call to another method (we'll get to that later).

Looking at the Method Body

// Method definition for setWidth()
public void setWidth( int newWidth ) 
{
   width = newWidth ;
}
There's only one assignment statement. The purpose of this method is to set the internal instance variable, width, to the parameter, newWidth.

Recall that the parameter is being provided to us from an object user. If the object user wants the width to be 10, then the parameter variable newWidth is set to 10.

However, parameter variables disappear after the method called. Instance variables do not. So if we want to record this information in the object, we must copy the value from the parameter variable to the instance variable.

Thus, the assignment statement:

   width = newWidth ;
setHeight should be easy to write.

Methods That Return a Value

Let's implement getWidth().
// Method definition for getWidth()
public int getWidth() 
{
   return width ;
}
You see a return keyword, followed by an expression. This is called a return statement.

When Java runs a return statement, it does the following:

If there are any statements after the return statement, it never gets run, assuming the return statement ran.
// Method definition for getWidth()
// println() statement never runs
public int getWidth() 
{
   return width ;
   System.out.println( "NEVER GETS HERE" ) ;
}
For example, this code never prints the message "NEVER GETS HERE". That's because running the return statement exits the method with the return value. There's no reason to put code after a return statement that definitely runs, since they have no purpose.

Return Values That Must Be Computed

Both getWidth() and getHeight() are very easy methods. They merely return the value of the instance variables inside the object. Notice that values are returned, not variables.

What if some computation is required to figure out the return value? For example, let's look at the code for getPerimeter().

// Method definition for getPerimeter()
public int getPerimeter() 
{
    return 2 * ( width + height );
}
The perimeter is twice the width plus twice the height.

A return statement can be an expression, provided the type of the expression is the compatible with the return type. In this case, the type of the expression is int and so is the return type. So, they are compatible.

Do you think that methods with return values can only have a single return statement? Can the method body be more complex?

Yes, it can. You can have several statements. Here's how it looks:

// Alternate method definition for getPerimeter()
public int getPerimeter() 
{
    int twiceWidth = 2 * width ;
    int twiceHeight = 2 * height ;
    return twiceWidth + twiceHeight ;
}
This time, we have two declarations, followed by a return statement. This does the same thing as the previous version of the method. However, it takes three statements instead of one.

Many would write the first version with the single return statement because it is shorter.

Many method definitions are this simple, but certainly there are method definitions that are quite a bit more complex.

From Method Call to Method Definition (and Back)

Let's imagine a method call for a Rectangle variable called rect with height 4 and width 10. This is how the method call looks:
   int x = 4 ;
   rect.setWidth( 2 * x ) ;
The method call is: rect.setWidth( 2 * x ). Assume this code appears in main() of a class called RectangleTest.

Step 1: Evaluate Arguments

First, we evaluate the argument. 2 * x evalates to 2 * 4 which evaluates to 8.

So, now the method call is: rect.setWidth( 8 ). We are passing 8 to the setWidth method.

Step 2: Jump to Method Definition

The argument value, 8, is used to initialize the parameter, newWidth. Thus, newWidth is now a variable storing 8.
public void setWidth( int newWidth ) 
{
   width = newWidth ;
}

Step 3: Run Method Body

Then, we enter the method. We only have the assignment statement.
   width = newWidth ;
In an assignment statement, we evaluate the RHS. The RHS contains newWidth, a parameter variable, which evaluates to 8.

This is then copied to the instance variable, width, which is set to 8, replacing the old value of 10.

Since there are no more statements, we head back to the method call.

Step 4: Jump Back to Method Call

We jump back. There's no return value, so we're done with the method call, and can run the next statement.

Tracing a Method Call with a Return Value

In the previous section, we just did a trace. That is, we ran the program just as the JVM (Java Virtual Machine) would run it (although it does it much faster).

Let's try another method call, this time with a return value.

   int perim = rect.getPerimeter() ;
Assume the width is 5, and the height is 10.

Step 1: Evaluate Arguments

Nothing to evaluate, so we're done.

Step 2: Jump to Method Definition

Normally, we'd initialize the parameter variables. There are none, so we can go to the next step.

Step 3: Run Method Body

The method definition looks like:
public int getPerimeter() 
{
    return 2 * ( width + height );
}
We have a single return statement. We evaluate the return expression. This means we evaluate 2 * ( width + height ). width is an instance variable with value 5, as assumed earlier. height is an instance variable with value 10, as assumed earlier.

Plugging those values in, we get 2 * (5 + 10). The final evaluated result is 30. This value is returned.

Step 4: Jump Back to Method Call

We jump back.
   int perim = rect.getPerimeter() ;
A method call with a return value evaluates to the return value. The return value of rect.getPerimeter() is 30. So, we replace the method call by its return value, to get
   // rect.getPerimeter() evaluates to 30, in this case
   int perim = 30 ; 
So, perim gets the value 30, in the assignment statement.

Summary

It's very important you understand the steps to perform a method call. When you start debugging your code, you need to know what happens. Here are the steps: You may have to look at two different files. The method call usually occurs in the main() of one class, and calls the method definition, which is usually in a different class. This means looking back and forth. A good IDE lets you jump between files easily, by letting you click on the method call to get to the method definition.