Lecture 2: Classes, Objects, Interitance, Scope, Wrappers

Balasubramanian Narasimhan
Department of Statistics
Stanford University
Stanford, CA 94305

$Revision: 1.2 $ of $Date: 2000/01/20 23:26:39 $

Table of Contents

Abstract

In the last class, we gave a brief introduction to Java. We continue to discuss Classes, Objects, related topics, and more on the Java Development Environment. I will conclude by building a simple wrapper program for and existing program. This, of course, does not gain you the platform independence, but one can use this cleverly to create better user interfaces.

Classes and Objects

[*]

In the last class, we gave an example of a Simple Linear Regression Model class with much stuff omitted. I present a modified version of the class below which is a bit more useful.

<SimpleLinearRegressionModel.java>=
public class SimpleLinearRegressionModel {
  /**
   * The predictor
   */
  double[] x;

  /**
   * The response
   */
  double[] y;

  <SLR Constructors>
  <SLR Methods>
}

By default, when one writes a class, one extends the root object and this need not be made explicit. (I'll talk more about inheritance below.) Note how I have added comments to describe the variables I use. The variables in a class are called fields and the routines in a class are called methods.

Let us continue to flesh this out.

Constructors

[*]

Constructors are methods one uses to construct instances of the objects. There is no requirement that one write constructors. If you don't the constructor defaults to the ones of the ancestor, the root object in this case. However, that is not going to be too useful, is it? So let us write some constructors.

<SLR Constructors>= (<-U) [D->]
/**
 * Constructor
 * Constructs a Simple Linear Regression Model (no checks are made on params)
 *
 * @param argX the predictor variable
 * @param argY the response variable
 */ 
public SimpleLinearRegressionModel(double[] argX, double[] argY) {
        x = argX;
        y = argY;
        compute();
}

So there we have a constructor although prudent programmers should check things before running off to compute stuff.

We can have more than one constructor for convenience. For example, often data comes as a two-dimensional array, so another constructor utilizing that might be convenient. Here goes:

<SLR Constructors>+= (<-U) [<-D]
/**
 * Constructor
 * Constructs a Simple Linear Regression Model (no checks are made on params)
 *
 * @param argData a 2d array of predictor (row 0) and response (row 1)
 */ 
public SimpleLinearRegressionModel(double[][] argData) {
        x = argData[0];
        y = argData[1];
        compute();
}

Of course, a more realistic constructor here would be one that forces the intercept to zero.

In Java, you are allowed to have many methods, or constructors that have the same name, but have different signatures. The signature of a method is a tuple: the name of the method, with the number and types of each of its argument. Even order can change signature.

Let us complete the compute() routine.

<SLR Methods>= (<-U) [D->]
    /**
     * The mean of x
     */
     double xBar;

    /**
     * The mean of y
     */
     double yBar;

    /**
     * The slope
     */
     double slope;

    /**
     * The intercept
     */
     double intercept;

    /**
     * The std dev of x
     */
     double sx;

    /**
     * The std dev of y
     */
     double sy;


    /**
     * A convenience method to compute quantities for SLR
     */
    private void compute() {
      int n = x.length;
      double sumy = 0.0,
             sumx = 0.0,
             sumx2 = 0.0,
             sumy2 = 0.0,
             sumxy = 0.0;

      for (int i = 0; i < n; i++) {
        sumx += x[i];
        sumx2 += x[i] * x[i];
        sumy += y[i];
        sumy2 += y[i] * y[i];
        sumxy += x[i] * y[i];
      }
    
      xBar = sumx / n;
      yBar = sumy / n;
    
      slope = (sumxy - sumx * yBar) / (sumx2 - sumx * xBar);
      intercept = yBar - slope * xBar;
      sx = Math.sqrt((sumx2 - sumx * xBar) / (n - 1));
      sy = Math.sqrt((sumy2 - sumy * yBar) / (n - 1));
    }

Finally we create some methods to access the computed quantities.

<SLR Methods>+= (<-U) [<-D]
  /**
   * Get the slope for this model
   *
   * @return the slope
   */
  public double getSlope() {
    return slope;
  }

  /**
   * Get the intercept for this model
   *
   * @return the intercept
   */
  public double getIntercept() {
    return intercept;
  }

  /**
   * Get the R-squared for this model
   *
   * @return r-squared
   */
  public double getRSquared() {
    double r = slope * sx / sy;
    return r * r;
  }

  /**
   * Get the x array 
   * This method is used purely to illustrate object reference in the
   * discussion below.
   *
   * @return the x array
   */
  public double[] getX() {
    return x;
  }

Using the Simple Linear Regression Model

[*] Here is a simple main program that actually uses the class.

<MainSLR.java>=
public class MainSLR {
    public static void main(String[] args) {
      double[] x = {38, 56, 59, 64, 74};
      double[] y = {41, 63, 70, 72, 84};
      SimpleLinearRegressionModel slr = new SimpleLinearRegressionModel(x, y);
      System.out.println("Model is y = " + slr.getIntercept() + 
                         " + " + slr.getSlope() 
                         + " * x with R-Squared = " + slr.getRSquared());
    }
}

Later, we'll make this more useful by having it read data from files, urls, etc.

Remarks

[*]

Extending a Class

[*]

Extending classes is one way of resusing code and methods. If ClassA contains most of the functionality you want, then you can extend ClassA to ClassB and override or add new methods you need. Consider the Point example from the previous lecture.

<Point Class>=
public class Point {
   double abscissa;
   double ordinate;

   public void clear() {
     abscissa = 0;
     ordinate = 0;
   }

   public void move( double x, double y) {
     abscissa = x;
     ordinate = y;
   }
}

A Pixel class can naturally extend the Point class:

<Pixel Class>=
public class Pixel extends Point {
   Color color;

   public void clear() {
      super.clear();
      color = null;
   }
}

The pixel class overrides the clear() method of Point. BTW, I have seen people confuse override with erase. No erasure is taking place here since Point still has a reference to its clear() method.

Point is the superclass of Pixel and Pixel is a subclass of Point. Note that a Pixel can be both a Point and a Pixel, that is, it can have many forms. This feature is called Polymorphism.

In Java, we have single inheritance: a class can extend only one class.

Scope

[*]

You have seen a few qualifiers in the code above: public, which means that a method or field is publicly accessible to all classes, private meaning the field or method is accessible only in the class itself. Here is a list of qualifiers:

Public
Access is allowed from anywhere the class is accessible and this access is inherited by subclasses.
Private
Access is allowed only in the class itself.
Protected
Access is allowed from the class itself and other subclasses and even code in the same package.
Package
By default, no access qualifier means that access is allowed only from subclasses in the same package.

Generally, I use private access for fields with the exception and provide get set methods to access the field. When designing libraries though, for efficiency, I might use protected.

Sometimes, you want either a method or a field to be defined once for the whole class, to be shared between different instances of the object. When you declare a field static, every instance of that class shares the same field. This is not something you want to do unless you really understand it well. Injudicious use of static keyword often causes bewildering compile errors that takes a while to understand.

Documentation with Javadoc

[*]

By following some conventions in documenting your classes, you get the benefit of automatically generated documentation. For example, on our system rgmiller.stanford.edu I ran the following command

<Javadoc generation>=
/usr3/java2/bin/javadoc -d docs -private SimpleLinearRegressionModel.java

and got a complete documentation for the code in html.

For more on javadoc and conventions, see http://java.sun.com/products/jdk/1.2/docs/tooldocs

Wrappers

[*]

Java is very useful as a system administration language as well. Here is a simple wrapper around the whois program.

<WhoIS.java>=
/**
 * WhoIs.java
 *
 *
 * Created: Thu Jan  6 15:44:02 2000
 *
 * @author Balasubramanian Narasimhan
 * @version 1.0
 */

import java.io.*;

public class WhoIs {
    
  public static void main(String[] args) {
        Runtime r = Runtime.getRuntime();
        BufferedReader bir = 
            new BufferedReader(new InputStreamReader(System.in));
        String line;
        try {
            while ((line = bir.readLine()) != null) {
                Process p = r.exec("whois " + line);
                BufferedReader bpr 
                    = new BufferedReader(new InputStreamReader(p.getInputStream()));
                while ((line = bpr.readLine()) != null) {
                    System.out.println(line);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
  }
} // WhoIs

A slightly modified version strips the annoying lines off before presenting the output.

<WhoIs2.java>=
/**
 * WhoIs2.java
 *
 *
 * Created: Thu Jan  6 15:44:02 2000
 *
 * @author Balasubramanian Narasimhan
 * @version 1.0
 */

import java.io.*;

public class WhoIs2 {
    
    public WhoIs2() {
        
    }
    
    public static void main(String[] args) {
        Runtime r = Runtime.getRuntime();
        BufferedReader bir = 
            new BufferedReader(new InputStreamReader(System.in));
        String input;
        try {
            while ((input = bir.readLine()) != null) {
                Process p = r.exec("whois " + input);
                BufferedReader bpr 
                    = new BufferedReader(new InputStreamReader(p.getInputStream()));
                boolean print = true;
                String output;
                while ((output = bpr.readLine()) != null) {
                    if (print) {
                        if (output.startsWith(" -------")) {
                            print = false;
                        } else {
                            System.out.println(output);
                        }
                    } else if (output.startsWith(" --------")) {
                        print = true;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} // WhoIs2
It is only a little more work to create a Windowing interface where more stuff can be done. In the next lecture, we will address creating a GUI.

Indices

[*]

Code Chunks

[*] This index is generated automatically. The numeral is that of the first definition of the chunk.