CSCI A202 - Introduction to Programming (II)

Lecture 3: Constructors. Reference Types and Linked Lists.

Brief summary of things so far

So far we have looked at methods, classes, and objects. We now know that programs in Java are essentially a collection of class defintions, which are compiled and run. We have said that there are three things that you should expect to see in a class definition:

We have compared classes with factories that contain blueprints for manufacturing gadgets (objects). The collection of instance members that appear in the class definition represents the blueprint.

One can create new objects of a certain class with the new operator, which invokes a constructor. So far we have said we would not worry about that since if we don't specify any constructor for a class Java will provide a default one that has no arguments and that (for all we care right now) does nothing.

When a constructor is invoked the blueprint is checked against, and an object is produced right away. According to the blueprint (which contains the descriptions of all the instance members) the newly created object has its own set of instance members. When we rely on the default no-args constructor the instance variables are initialized to their default variables.

The initial state of an object can be customized by providing specialized constructors, and we will see a few examples of that today. The rule that we have to keep in mind is that if at least one constructor is provided with the class definition there is no default constructor available any longer.

So we need to remember that instance members are one per object, while class (or static) members are one per class. That means that class variables are global to all objects. The purpose of the class variables is to keep state for the entire factory.

Among all static methods there is one that plays a special role: it runs the show. It must be defined as public and void and have the following signature

public static void main(String[] argv) {
  
} 
and it can be used as a starting point of your program.

Class methods cannot access instance members directly since there are as many instance members with the same name as created objects, so you can only access them through an object reference.

Class members can be accessed directly by instance methods. They can also be accessed through the class name or through a reference to an object of the class.

Since main runs the show you can put it separately in a class of its own that does not have any other members declared other than the main routine main. But it's perfectly OK to place your main in one of the classes that you've written for the program (see Point below) and in fact, as we will show later, each class can have its own main, which will enable you to run the show in more than one way (a technique that is sometimes called unit testing).

Constructors

Let's consider the following class definition:

class Point {
  double x; 
  double y; 
}
We can draw a picture of that.

Let's say we want to create a few Point's in a real program. We need a class method main in a class somewhere. Instead of using another class we could use class Point so we might as well develop it for it's value as a Point as well as to run the show (or the program).

So we have

class Point { 
  double x; 
  double y; 
  public static void main(String[] args) {
    Point p, q; 
    p = new Point();
    p.x = 3.0; p.y = -2.0;  
    q = new Point();
  } 
}
When this program is compiled and run the main method is invoked. It declares two variables of type Point and it places in each one of them a reference to two newly created Points. By default the instance members (the coordinates) of each of the Points are 0.0 and 0.0. To change the coordinates of the first one of the Points to (3.0, -2.0) we use the reference to the object and the dot operator to access and change each one of the instance variables to the desired values.

We can provide a constructor that does exactly the same thing, and use it.

class Point { 
  double x; 
  double y; 
  Point(double coord1, double coord2) {
    x = coord1; 
    y = coord2; 
  } 
  public static void main(String[] args) {
    Point p, q; 
    p = new Point(3.0, -2.0);    
    q = new Point(); // this will no longer work 
  } 
}
The first goal is achieved, but a problem arises. We can solve it in one of two ways: Of course to compile and run these tests you need to do:
javac Point.java
which creates a class Point.class and then run it with
java Point
depending on the platrform you're running on (NT or Unix).

Reference Types and Linked Lists

We start with the definition of a Pair.

class Pair {
  int value; 
  Pair next; 
}
We draw a picture of it.

It is not much different from Point.

The default value for reference types is null.

Let's write a customized constructor, one that creates Pairs with a specific initial value.

class Pair {
  int value; 
  Pair next; 
  Pair (int v, Pair n) {
    value = v; 
    next = n; 
  }
}
Let's play with this constructor to see how it works.
Pair p, q; 
p = new Pair(1, null); 
q = new Pair(2, null);
p.next = q; 
We draw a picture and realize that this creates a linked list 1, 2.

A shorter version would be

Pair p, q; 
p = new Pair(1, new Pair(2, null)); 
so we realize that we don't need q any longer.

Exercise

Consider the following code fragment:

Pair p = new(1, new (6, new (3, new (2, null))))
Draw a picture for the structure that p points to.

Now that we have Pair's we will create a List class. List objects contain one instance variable, which is a pointer to the first Pair in the list. They also must have a constructor that takes one argument (an int) and produces the desired number of Pairs the first one of which will be pointed at by the instance variable p (a better name would have been firstPair)

class List {
  Pair p;
  List (int size) {
    p = new Pair(0, null); 
    Pair aux = p; 
    for (int i = 1; i < size; i++) {
      aux.next = new Pair(1, null); 
      aux = aux.next; 
    } 
  } 
} 
The constructor assumes that the required size is greater than 0. It creates a first cell that has 0 as the value and then it creates the rest of the remaining cells and places in them consecutive integers. Thus, at the end, the instance variable p will point to a list of Pairs that will hold increasingly bigger values from 0 to size - 1. Our Lists differ only in size.

We will add an instance method length() that will enable List objects to reply to the question 'What's your length?' when asked about it.

int length() {
  int result = 0; 
  if (p == null) return result; 
  else {
    for (Pair aux = p; aux != null; aux = aux.next) 
      result += 1; 
    return result; 
  }
}
Essentially the function counts the number of cells by walking on them with the help of auxiliary variable aux.

Let's set things up for the method that runs the show.

We create a class called ListProcessor

class ListProcessor {
  public static void main(String[] args) {
    List l = new List(6); 
    System.out.println("The list has length " + l.length); 
  } 
} 
place all three classes in a file File.java in an empty directory and compile it. Then run class ListProcessor, which should print 6, the length of the created list.

Here is, for redundacy and summary, class List again, with the just defined instance method that computes the length of the list:

class List {
  Pair p;
  List (int size) {
    p = new Pair(0, null); 
    Pair aux = p; 
    for (int i = 1; i < size; i++) {
      aux.next = new Pair(i, null); 
      aux = aux.next; 
    } 
  } 
  int length() {
    int result = 0; 
    if (p == null) return result; 
    else {
      for (Pair aux = p; aux != null; aux = aux.next) 
        result += 1; 
      return result; 
    }
  }
} 
Note that length() is implemented iteratively.

We then change class ListProcessor by adding a method show() which given a first cell in a list walks through it and returns the textual representation of the list as a sequence of integer values separated by blanks.

class ListProcessor {
  public static void main(String[] args) {
    List l = new List(6); 
    System.out.println("New list: " + show(l.p)); 
    System.out.println("The list has length " + l.length()); 
  } 
  static String show(Pair p) { 
    if (p == null) { return " "; } 
    else { return p.value + " " + show(p.next); } 
  }
} 
Note that show is static (why?) and is implemented recursively.

So at this point we are able to create lists of any given length, compute their length and display them to screen. The length is computed through an instance method (of class List) implemented iteratively and the display function is implemented through a class method of class ListProcessor.

For your exercise we would like you to check your understanding of the program we have just developed by providing:

Putting all things together we will have the following program that you need to finish. It will be your first homework assignment, tentatively due at the beginning of the lab on Thursday, July 1. Your task is to provide the implementations for one recursive method and one iterative method. Feel free to study their respective counterparts for inspiration. Note the program compiles and run since the two empty methods return proper placeholders.

The desired behaviour is illustrated below:

tucotuco.cs.indiana.edu% javac File.java
tucotuco.cs.indiana.edu% java ListProcessor
The list is: 1 
The length:  1
The list is: 1  
The length:  1
tucotuco.cs.indiana.edu% java ListProcessor
The list is: 6 5 2 
The length:  3
The list is: 6 5 2  
The length:  3
tucotuco.cs.indiana.edu% java ListProcessor
The list is: 3 2 1 
The length:  3
The list is: 3 2 1  
The length:  3
tucotuco.cs.indiana.edu% java ListProcessor
The list is: 1 1 3 6 2 
The length:  5
The list is: 1 1 3 6 2  
The length:  5
tucotuco.cs.indiana.edu% 
The fragment below contains the code developed in the lecture, compiled and run.

tucotuco.cs.indiana.edu% ls -l Assignment.*
-rw-------   1 dgerman  students    1275 Jun 23 13:01 Assignment.java
tucotuco.cs.indiana.edu% cat Assignment.java
class ListProcessor {
  public static void main(String[] args) {
    List l = new List((int)(Math.random() * 6 + 1)); 
    System.out.println("The list is: " + l.show()); 
    System.out.println("The length:  " + l.length()); 
    System.out.println("The list is: " + show(l.p)); 
    System.out.println("The lenght:  " + length(l.p)); 
  }
  static int length(Pair p) {
    return -1; // implement recursive length(); 
 
  }
  static String show(Pair p) { 
    if (p == null) { return " "; } 
    else { return p.value + " " + show(p.next); } 
  } 
} 
 
class List {
  static final int SIZE = 6; 
  static final int VALUE = 10; 
  Pair p;
  List (int size) {
    p = new Pair((int)(Math.random() * VALUE + 1), null); 
    Pair aux = p; 
    for (int i = 1; i < size; i++) {
      aux.next = new Pair((int)(Math.random() * VALUE + 1), null); 
      aux = aux.next; 
    } 
  } 
  int length() {
    int result = 0; 
    if (p == null) return result; 
    else {
      for (Pair aux = p; aux != null; aux = aux.next) 
        result += 1; 
      return result; 
    }
  } 
  String show() {
    return " "; // implement recursive show(); 
 
 
 
 
 
 
 
 
 
  } 
}
 
class Pair {
  int value; 
  Pair next; 
  Pair(int val, Pair nextPair) {
    value = val; 
    next = nextPair; 
  } 
} 
tucotuco.cs.indiana.edu% ls -l *.class
No match
tucotuco.cs.indiana.edu% javac Assignment.java
tucotuco.cs.indiana.edu% ls -l *.class     
-rw-r--r--   1 dgerman  students     735 Jun 23 13:04 List.class
-rw-r--r--   1 dgerman  students    1285 Jun 23 13:04 ListProcessor.class
-rw-r--r--   1 dgerman  students     362 Jun 23 13:04 Pair.class
tucotuco.cs.indiana.edu% java ListProcessor
The list is:  
The length:  4
The list is: 6 6 4 6  
The lenght:  -1
tucotuco.cs.indiana.edu% java ListProcessor
The list is:  
The length:  5
The list is: 8 3 10 6 7  
The lenght:  -1
tucotuco.cs.indiana.edu%

Last modified: Jun 24, 1999