CSCI A201/A597

Lecture Notes Twenty-One

Second Summer 2000


The reading assignment for this is 270-291, and 298-301.
Mostly the lecture notes of yesterday are for your enjoyment and perusal. You should do that on your own, relatively slowly, and thoroughly.

In class we will finish the lecture notes of Thursday and then start chapter 7. We'll finish chapter 7 quickly, although there will be one important new thing we will touch on.

Recursion. The saying goes that "to understand recursion you first need to understand recursion".

That's just a humorous saying. Recursion, in fact, is easy, and thoroughly useful. Once you have a fixed point.

But we'll get to that shortly. Another thing we want to emphasize is that the quiz in QuizSite is due on Tuesday but it will stay open all week.

That means it's recommended that you do it before Tuesday, but you won't have to. The rest of the assignment, due Wednesday should be turned in on paper to Adrian.

We'll also use this time to tie some loose ends. For example the condition that expresses the definition of a bad move in Nim.

Yes, it was expressed as follows:
if (number <= 0 || ((number > height / 2) && (number != 1))) {
  System.out.println("***Bad move: you lose."); 
  System.exit(0); 
} else {
Or, just the condition, again:
(number <= 0 || ((number > height / 2) && (number != 1)))

Especially the last part was tricky. Using de Morgan's law we can reformulate this to represent a good move.

Yes, we write down its negation. Here it is (a good move):
(number > 0 && ((number <= height / 2) || (number == 1)))

Even if this is no easier to read than the first version you now have a choice. To me this looks better, easier to understand, clearer.

To me too. Now the last loose end before we jump into methods (chapter 7).

Here's a program: What does it do?
class Show {
    public static void main(String[] args) {
        String a = "class Show {\n     public static void "; 
	String b = "main(String[] args) {\n\n\n    }\n}"; 
	System.out.println(a + b); 
    } 
} 

It prints part of itself. Can you write a program that outputs itself, that is, that prints its source code exactly when run?

That will be the challenge for the remaining of the semester. Turn that in before the last lecture for big bonus.

How big? Big!

Can I write the program with what I have? Yes. The solution that will be shown in class will be using for loops, characters, and Strings.

All right, I'll work on that. Now let's do chapter 7.

Methods. You have already implemented several simple methods and are familiar with the basic concepts.

Let's go over parameters, return values, and variable scope in a more systematic fashion. We will also review some of the more technical issues, such as static methods and variables.

When we implement a method we define the parameters of the method.
Here's an example:
public class BankAccount {
  ...
  public void deposit(double amount) {
    ...
  }
  .. 
}

The deposit method has two parameters: one is explicit, called amount, and has type double. We expect to receive a value in it to be deposited.

The other one is implicit, and can be referred to by the the keywqord this. What we mean is that inside an instance method (like deposit) the keyword this will always refer to the host object.

So if it's always available and always in the same way, the this reference is not even mentioned in the list of parameters. But from any instance method we can use the keyword this to refer to the object that contains the method.

Therefore amount is called a formal parameter for the method deposit. When we want to deposit some money we need to know two things:

a) the account's name, and b) the amount of money

... and we need to actually invoke the method, ... in order to deposit the money.
// somewhere in main (or another method) 

myChecking.deposit(allowance - 200); 

In this example myChecking is an object of type BankAccount ... and allowance is probably a double.

Both should be declared and initialized before we use them. When deposit starts running the value of the expression
allowance - 200

... becomes the actual parameter or argument to the method, ... and will be known by the name of amount while the method is running.

When the method returns the formal parameter variables are abandoned, and their values are lost. The entire process is like a phone call: you call myChecking's deposit method...

... and you give it some input: thye amount that you want to deposit. It then starts working for you and you stay on the line.

When it's done it says so, before you hang up. Sometimes it returns a value before the two of you can end the conversation.

deposit only says when it's done, without returning anything. It is declared as void. void is its return type.

Yes: it does not return anything to its caller. Hence: void.

It only does what it is supposed to do, and then it says: "I'm done." Which is also called returning except not as in "returning a value",...

... rather, like in "returning from a trip". A trip to the bank.

Explicit parameter variables are no different from other variables. You can modify them during the execution of a method: but unless you have a good reason for that it is considered bad style.

Let's now consider a more complicated example:
public class BankAccount {
  ...
  public void transfer(BankAccount other, double amount) { 
    withdraw(amount); 
    other.deposit(amount); 
  } 
  ... 
} 
This method can be used to transfer money from one account to another.

Here's how we can use it:
momsSavings.transfer(myChecking, allowance);
I have one question before we go further though...

Yes, what is it? Weren't we supposed to write
this.withdraw(amount)
in the definition of the deposit method?

Yes, and please make the correction in your notes. OK, I've updated mine.

But why does it work though? Because it defaults to it, anyway.

I think that using this makes it more uniform and explicit. And I think so too.

How many formal parameters does this new function (method) have? Two of them are explicit: other and amount.

The first one is a BankAccount. The second one is a double.

And in addition to that the method will be able to access the object to which it belongs using this. Yes, this is always available in an instance method and means: "the object that contains this method", or "this method's host".

What happens when the method is invoked? When the method is invoked the reference to myChecking is copied into the method's other formal parameter...

... and allowance will be copied into amount. Note that both the object references and the numbers are copied into the method.

After the method exits the two bank account balances have changed. The method was able to change the accounts because it received copies of the object references.

Of course, the contents of the allowance variable was not changed. In Java no method can modify the contents of a number variable that is passed as a parameter.

What names should we give to the parameters? You can give any names you want to method parameters.

Choose explicit names for parameters that have specific roles; choose simple names for those that are completely generic. The goal is to make the reader understand the purpose of the parameter without having to read the method's description.

The compiler takes the types of the method parameters and return values very seriously. It is an error to call a method with a value of incompatible type, ...

... or use it in a context that is not compatible with its return type (if any). Java is a strongly typed language.

The compiler automatically converts from int to double and from ordinary classes to superclasses (see chapter 9). It does not convert, however, when there is a possibility of information loss...

... and does not convert between numbers and strings and objects. This is a useful feature, because it lets the compiler find programming errors before they create havoc when the program runs.

A method that accesses an object and returns some information about it, without changing the object, is called an accessor method. Such as getBalance.

In contrast, a method that modifies the state of an object is called a mutator method. deposit and withdraw are mutator methods.

You can call an accessor method ... as many times as you like.

If that's all you do, you will always get the same answer, and it does not change the state of the object. Some classes have been designed such that objects of that kind have only accessor methods and no mutators at all.

Such classes are called immutable. An example is the String class.

Once a String has been constructed, its contents never change. For example, the substring method does not remove characters from the original string.

Instead it constructs a new string that contains the substring characters. Here's another example of an accessor method that simultaneously looks at two objects:
public class BankAccount {
  public boolean equals(BankAccount other) {
    return (this.getBalance() == other.getBalance()); 
  }
} 

It makes use of two other accessors (or, rather, the same accessor invoked on two different objects) and compares the values that they return. It returns the truth value that comes out of the comparison.

So we could use it as follows: Very good.
if (account1.equals(account2)) {
  // they have the same balance... 
} else {
  // they do not have the same balance... 
} 

In general the expectation is that accessor methods do not modify any parameters, ... ... and that mutator methods do not modify any parameters beyond this.

This ideal situation is not always entirely the case. For example the transfer method discussed before.

It changes its this, ... ... while also updating the other account.

Such a method is said to have a side effect. A side effect of a method is any kind of observable behaviour outside the object.

In an ideal world, all methods would be accessors that simply return an answer without changing any value at all. In fact, programs that are written in so-called functional programming languages,

... such as Scheme or ML, ... come close to this ideal.

Of course, in an object oriented programming language, we use objects to remember state changes. Therefore, a method that just changes the state of its implicit parameter is certainly acceptable.

A method that does anything else is said to have a side effect. While side-effects cannot be completely eliminated, they can be the cause of surprises and problems and should be minimized.

Sometimes you write methods that don't work on any particular object. Such a method is called a static method or a class method and needs to be declared as static.

In contrast, methods such as getBalance, withdraw, and deposit in the preceding sections are often called instance methods, ... because they operate on particular instances of an object.

There's one of each in each object (of that type) that gets created. Math.sqrt is a static method.

And every application must have a static method called ... main.

Correct. Here's another example, that involves only numbers:
class NumericMethods {
  public static boolean approxEqual (double x, double y) {
    final double EPSILON = 1E-14; 
    double xymax = Math.max(Math.abs(x), Math.abs(y)); 
    return Math.abs(x - y) <= EPSILON * xymax;
  }
  // more numeric methods could come here... 
} 
This method encapsulates computation that involves no objects at all, only numbers (and booleans), hence only primitive types.

To call (or use) a static method you need to supply the name of the class, for example:
double r = Math.sqrt(2); 
if (NumericMethods.approxEqual(r * r, 2)) 
  System.out.println("Math.sqrt(2) is approx. 2"); 
... same as we do with Math.sqrt.

Now we can tell you why the main method is static: ... when the program starts there may not be any object at all.

Therefore the first method in a program must be a static method. Good enough.

To summarize our knowledge about static methods we can say that... ... a static method is a method that does not belong to any object, and that has only explicit parameters.

Let's look at some examples now. What does the following example illustrate?
public class Example {
  public static void addOneToIt (int number) {
    System.out.println(number); 
    number = number = 1; 
    System.out.println(number); 
  } 
  public static void main(String[] args) {
    int value = 3; 
    System.out.println(value); 
    Example.addOneToIt(value); 
    System.out.println(value); 
  } 
} 
Let's walk through the method call. When the call is made the parameter value is set to the same value as the argument.

The value is copied. Changes to it are not seen outside.

That's all there is to it. Easy.

Is there a moral to it? In Java method parameters are copied into the parameter variables when a method starts.

Computer scientists call this call mechanism "call by value". As you have seen there are some limitations to the "call by value" mechanism.

It is not possible to implement methods that modify the contents of number variables. Other programming languages support an alternate mechanism, called "by reference".

This involves passing only the address to where the number variable is stored. This is what happens when you pass an object as an actual parameter.

Let's see an example. Oh, boy. I like examples best.
class NumberHolder {
  int value = 1; 
} 
class Example {
  public static void main(String[] args) {
    NumberHolder n = new NumberHolder();
    System.out.println(n.value); 
    Example.addOneToIt(n); 
    System.out.println(n.value); 
  } 
  public static void addOneToIt (NumberHolder n) {
    n.value = n.value + 1; 
  } 
} 
But we've seen this before, haven't we?

Yes, when we discussed copying of variables. Primitive types are copied by value, while reference types are copied by reference.

Good enough. References though are still passed by value.

Understood. Can we see an example? Oh boy -- that's what I like best.
class Pair {
  double x; 
  double y; 

  Pair(double x, double y) {
    this.x = x; 
    this.y = y; 
  }   

  void report() { 
    System.out.println("Hello! I'm at: (" + x + ", " + y + ")"); 
  }
} 

class Testing {
  public static void main(String[] args) {
    Pair a = new Pair(100, 0); 
    Pair b = new Pair(0, 100); 
    a.report(); 
    b.report(); 
    Testing.swap(a, b); 
    a.report(); 
    b.report(); 
  } 

  static void swap(Pair a, Pair b) {
    Pair temp = a; 
    a = b;
    b = temp; 
  }
} 

I like it. Easy and understandable. But it still gives you a level of indirection.

Yes. You can, at least in principle, get inside those Pairs. Let's summarize: a Java method cann update an object's state

... using the reference to it, ... but it cannot replace the contents of an object reference.

This shows that object references are passed by value in Java. Although we can safely say that objects themselves are passed

... by reference. Exactly.

Except that the reference itself is copied. Copied, yes -- but pointing to the same thing the original one was.

Fair enough. The distinction is clear now.

A method that has a return type other than void must return a value, by executing a statement of the form:
return <expression> ;
Been there, done that.

Yes, but let's see if we can come up with something new. Well, for one thing, you can return the value of any expression.

You don't need to store the result in a variable and then return the variable. When a return is processed, the method exits immediately.

This is convenient for handling exceptional cases in the beginning.
Oh, yes, here's an example:
public static int fibo (int n) {
  if (n == 1) 
    return 1; 
  else if (n == 2) 
    return 1; 
  else {
    int fOlder = 1; 
    int fOld = 1; 
    int result = fOld + fOlder; 
    for (int i = 3; i <= n; i++) {
      result = fOld + fOlder; 
      fOlder = fOld; 
      fOld = result; 
    } 
    return result; 
  } 
} 

It is important that every branch of a method return a value. Also, a method whose return type is not void always needs to return a value.

If the method contains several if/else branches make sure that each one of them returns a value. At the end of every possible path through a non-void method there should be a return statement,

... returning the value of an expression of compatible type.
For example is this right?
public static int fibo (int n) {
  if (n <= 0) 
    System.out.println("Incorrect argument!");
  else if (n == 1) 
    return 1; 
  else if (n == 2) 
    return 1; 
  else {
    int fOlder = 1; 
    int fOld = 1; 
    int result = fOld + fOlder; 
    for (int i = 3; i <= n; i++) {
      result = fOld + fOlder; 
      fOlder = fOld; 
      fOld = result; 
    } 
    return result; 
  } 
} 

It is not, because if the argument is negative we don't return anything. What should we return, then?

I don't know, what do you think of this one?
return Math.round(Math.pow((1 - Math.sqrt(5))/ 2, n))
Ha, that was a good one!

Or we should throw an Exception. Yes, but about those perhaps some other time...

We have now encountered the four kinds of variables that Java supports.
  1. Instance variables
  2. Static variables
  3. Local variables
  4. Parameter variables

The lifetime of a variable defines when the variable is created and how long it stays around. When an object is constructed, all its instance variable are created.

As long as the object is around its instance variables are around, with the object, inside the object. A static variable is created when its class is first loaded, and it lives as long as the class.

A local variable is created when the program enters the statement that defines it. It stays alive until the block that encloses the variable definition is exited.

Here's an example:
public void withdraw (double amount) {
  if (amount <= balance) {
    double newBalance = balance - amount; 
    // local variable newBalance created and initialized
    balance = newBalance;
  } // end of lifetime of local variable newBalance 
} 
If you tried to print newBalance right before the end of the method you'd get an error.

Yes, and the reason is: it's known only in the then branch of the if statement. Inside the inner pair of curly braces.

Finally, when a method is called, its parameter variables are created. They stay alive until the method returns to the caller.

Next, let us summarize what we know about the initialization of these four types of variables. Instance variables and static variables are automatically initialized with a default value...

... which is 0 for numbers, false for boolean and null for objects, Yes. So instance variables and static variables are automatically initialized with a default value...

... unless you specify another initial value. Very good.

Parameter variables are initialized with copies of the actual parameters. That's when the method gets called.

Local variables are not initialized by default. For local variables you must supply an initial value, and the compiler complains if you try to use a local variable that you never initialized.

The scope of a variable is that part of a program that can access it. The part of the program in which you can access it, the variable, is the scope of the variable, yes.

OK. As you know, instance and static variables are usually declared as private, and you can access them only in the methods of their class. I see... Scope answers to the question: can I see it?

The scope of a local variable extends from the point of its definition to the end of the enclosing block. The scope of a parameter variable is the entire body of its method.

Now let's look a bit closer to a few situations. We're going to go through a few examples.

It sometimes happen that the same variable name is used in two methods:
public static double area(Rectangle rect) {
  double r = rect.getWidth() * rect.getHeight();
  return r; 
} 

public static void main(String[] args) {
  Rectangle r = new Rectangle(5, 10, 20, 30); 
  double a = area(r); 
  ... 
} 
These variables (the two r's) are independent of each other.

You can have variables with the same name r in different methods, ... just as you can have different motels with the same name (let's say, "Super 8") in different cities.

In this situation the scopes of the two variables are disjoint. Problems arise, however, if you have two or more variable names with overlapping scope.

Like when you have two Kroger's in the same city. Almost, but not exactly. In Java this situation is called shadowing.

There are rules in the language that tell you which one of the variables you will be referring to if you use the ambiguous name. Can we see some examples?

Certainly.
class Employee {
  String name;
  Employee (String name) {
    this.name = name; 
    // this is mandatory not just good style here!!
  } 
} 
The parameter, which is like a local variable, shadows the instance variable.

The Java language specifies that when there is a conflict between a local variable name and an instance variable name the local variable wins out. This sounds pretty arbitrary but there is actually a good reason.

You can still refer to the instance variable using this Which you should do anyway.

Do you have any questions? No, but I have something close to that.

An example! You bet.

Consider this:
class Puzzle {

  public static void main(String[] args) {
    Puzzle p = new Puzzle(); 
    System.out.println("Final result: " + p.fun(6)); 
  }   
         
  int fun(int n) { 
    int result; 
    if (n == 0) return 0; 
    else {
      // [1] 
      result = n + fun(n - 1); 
      // [2] 
      return result; 
    } 
  } 

} 

Neat. What do we do with it? Well, what's the program computing?

Once you figure that out replace the first comment with a println statement that prints the variable n. Explain the change in output.

Then go back to the original code, and replace the second comment with the println statement that prints the variable n. Explain the change in output.

Then notice the name of the function. I know, I know, this was a lot of fun...

Last updated: July 24, 2000 by Adrian German for A201