CS 1713 Week 4:
More Object Oriented Design

Objectives:

Activities:

Reading: Chapter 7.5, 8.1-8.3, Chapter 3.9-3.11


Terminology: what is a class?

You can think of a class as a blueprint for making an object.
Look at the Rectangle class.

The String class

You have been using the String class.

Like other classes, you can make object by using new:
String s = new String("Hello, how are you?");
but it is more common to use a String literal:
String s = "Hello, how are you?";
Here are some of the String methods that you need to know:

You should become expert at using all of these.

indexOf returns the position of the first occurrence of the character or string parameter.
It returns -1 if not found.

Example 1:
Write a method that finds the position of the first 'a' in a String or returns -1 if the string does not contain the letter a.
Method 1:

   public static int findA(String s) {
      for (int i=0;i<s.length();i++)
         if (s.charAt(i) == 'a')
            return i;
      return -1;
   }
Method 2:
   public static int findA(String s) {
      return s.indexOf('a');
   }
Strings should be compared using the equals method, not by using ==.

compareTo is used to compare two strings, indicating which comes first.

Lexicographic order:

Lexicographic order is a generalization of alphabetical order. Java represents characters by Unicode (see Appendix C of the textbook). In this ordering, numbers come before letters and capital letters come before lower case letters. Lexicographic comparison is like alphabetizing the strings.

The compareTo compares strings lexicographically. The expression string1.compareTo(string2)

Example 2: Here are the results of some comparisons:

     Expression                  Value             Reason  
 ==============================================================
 "XYZ".compareTo("ABC")       Positive      'X' is greater than 'A'
 "XYZ".compareTo("XYZ")       Zero          "XYZ" equals "XYZ"
 "ABC".compareTo("xyz")       Negative      'A' is less than 'x'
 "1234".compareTo("56")       Negative      '1' is less than '5'
 "acts".compareTo("aces")     Positive      't' is greater than 'e'
 "aces".compareTo("ace")      Positive      "aces" begins with "ace"
 "ace".compareTo("aces")      Negative      "aces" begins with "ace
 "abc".compareTo("XYZ")       Positive      upper case letters before lower case

Look carefully at the last example because it may not work they way you think.
In the ordering used, all upper case letters come before all lower case letters, so when sorting, "abc" will come after "XYZ"!


Case Study 4: Implementing the Counter class

Create a project called cs04. You will create two classes in the counter package.

A counter has-a count that can be incremented and decremented. The count can be set to zero. The value of the count can be displayed. Initially the value of the count is zero.

Part 1: Implement the basic class

Part 2: Enhance the class

The Counter class illustrates polymorphism in object-oriented programming. That is, the Counter class has more than one form of the constructor and more than one form of methods with the same name.

You can run the Java Simulator on this case study here.


Static methods and attributes

The keyword static designates an item that is associated with the entire class rather than an individual object in the class. For example, the methods of the Math class are static. We don't need to instantiate a Math object to call them.

Example 3: To find the absolute value of a number, use the static method Math.abs.

   double x = -3.0;
   double y;
   y = Math.abs(x);

Attributes can also be static, meaning there is only one copy for the entire class.

Example 4: The Sheep class uses a static variable count to keep track of how many Sheep objects have been created.
Each sheep object keeps track of is own number in myNumber.
Each sheep can say baa and keeps track of the number of times it does in baaNumber. Add the following Sheep class to the cs04 project. Put it and a SheepTester class in the sheep project.

public class Sheep {
  private static int count = 0;
  private int myNumber;
  private int baaNumber;
  public Sheep( ) {
    count++;
    myNumber = count;
    baaNumber = 0;
  }

  public static int getCount() {
     return count;
  }

  public void sayBaa() {
    System.out.println("Baa!!!!");
    baaNumber++;
  }

  public int getBaaNumber() {
     return baaNumber;
  }

  public String toString( ) {
    return "I am sheep " + myNumber + ", my baa number is " + baaNumber;
  }
}

Exercise 1: Write client code to create 2 Sheep and have one of them say baa twice. Compare the use of static versus non-static qualifiers.
Ans: Here is some sample code:

public class SheepTester {
  public static void main(String[] args) {
    Sheep s = new Sheep();
    System.out.println("Sheep s is " + s);
    System.out.println("The number of sheep is " + Sheep.getCount());
    Sheep s2 = new Sheep();
    System.out.println("Sheep s2: " + s2);
    s.sayBaa();
    s.sayBaa();
    System.out.println("s is now " + s);
    System.out.println("The baaNumber of s is " + s.getBaaNumber());
    System.out.println("The number of sheep is " + Sheep.getCount());
  }
}
The static getCount can be referenced through the class name, Sheep. The non-static getBaaNumber and sayBaa are referenced by an object.

You can run this case study on the Java Simulator here.

Exercise 2: Why do object-oriented programmers have trouble getting to sleep at night?
Ans: Because they ask the sheep to count themselves :-)


Case Study 7: The RationalNumber class

We will create a project called cs07. All of the classes in this project will be in the rational package.
For this case study we will start with a skeleton which is available here.
Download this zip file and put it in your working/java directory on your Z drive. The name of this file is cs07-skeleton.zip.

Load this into eclipse as follows:

A rational number has a numerator and denominator. Two rational numbers can be added, subtracted, multiplied or divided to produce a new rational number. A rational number can also produce its reciprocal. Rational numbers can be tested for equality. Rational numbers are stored in reduced form (i.e., common factors from numerator and denominator are removed).

The RationalNumber class has the following public methods:
   public int getNumerator( );
   public int getDenominator( );
   public RationalNumber add(RationalNumber op2);
   public RationalNumber subtract(RationalNumber op2);
   public RationalNumber multiply(RationalNumber op2);
   public RationalNumber divide(RationalNumber op2);
   public RationalNumber reciprocal( );
   public boolean equals(RationalNumber op2);
   public String toString( );
Implement the methods as indicated. Also develop a RationalNumberTester class to test the RationalNumber class.

The skeleton uses the following private methods to reduce the numerator and denominator:
   private void reduce() {
      if (numerator != 0) {
         int common = gcd(Math.abs(numerator), denominator);
         numerator = numerator / common;
         denominator = denominator / common;
      }
   }

   private int gcd(int num1, int num2) {
      while (num1 != num2)
         if (num1 > num2)
            num1 = num1 - num2;
         else
            num2 = num2 - num1;
      return num1;
   }
Adjust the stored numerator and denominator to be in reduced form with the sign associated with the numerator.


Interfaces

The term public interface refers to the public methods through which we can interact with an object.

An abstract method is a method that only has a header and no implementation. Interfaces and any class containing abstract methods cannot be instantiated.

Example 5: The Java Comparable interface allows objects to advertise that they can be compared:
   public interface Comparable {
       public int compareTo(Object obj);
   }

Look at the Java documentation for the Comparable interface at http://java.sun.com/j2se/1.5/docs/api/java/lang/Comparable.html. According to the documentation:

The Comparable interface is used to order lists of objects.

Example 6 The String class implements the Comparable interface. We can use the compareTo method to put a list of String objects in lexicographical order. Look at the Java documentation for String at http://java.sun.com/j2se/1.5/docs/api/java/lang/String.html. Notice that the String class implements three difference interfaces.

Example 7: The Integer class is called a wrapper class because its job is to make an integer value look like an object, i.e. it "wraps" an int in object's clothing. Look at the documentation for the Integer class at http://java.sun.com/j2se/1.5/docs/api/java/lang/Integer.html. There are similar wrapper classes for the other primitive types called Double, Boolean, Character, Byte, Float, etc.

Exercise 3: What are the values cValue1 and cValue2 after the following code is executed?
   Integer x1 = new Integer(3);
   Integer x2 = new Integer(5);
   int cValue1 = x1.compareTo(x2);
   int cValue2 = x1.intValue() - x2.intValue();
Ans: Both values are negative. The value of cValue2 is, in fact, the numerical difference between the underlying values of x1 and x2 (e.g., -2). The documentation for Integer does not guarantee that compareTo's negative value is the actual difference, so we know only that cValue1 is negative.

Exercise 4: A name has a first name and a last name. Develop a Name class that implements Comparable.
Ans:
public class Name implements Comparable {
   private String firstName;
   private String lastName;

   public Name(String first, String last) {
      firstName = first;
      lastName = last;
   }

   public String getLastName() {
      return lastName;
   }

   public String getFirstName() {
      return firstName;
   }

   public int compareTo(Object obj) {
      Name otherName = (Name) obj;
      int lastCompare = lastName.compareTo(otherName.lastName);
      int firstCompare = firstName.compareTo(otherName.firstName);
      if (lastCompare != 0)
         return lastCompare;
      else
         return firstCompare;
   }

   public String toString() {
      return firstName + " " + lastName;
   }
}

Exercise 5: What happens in Exercise 4 if the following statements are executed?
   Name myName = new Name("John", "Doe");
   int answer = myName.compareTo("Jane");
Ans: You will get a ClassCastException when the first statement in compareTo is executed.

Exercise 6: What would change if a middle initial were added to Name?
Ans: The following modifications would need to be made:

Exercise 7: Modify RationalNumber so that it implements Comparable.

Exercise 8: Suppose that you were developing the Java Integer class. How would you implement compareTo?
Ans: After casting the incoming object to Integer, just subtract the value of this object from that of other object:
   public int compareTo(Object obj) {
      Integer otherObj = (Integer)obj;
      return intValue() - otherObj.intValue();
   }


Frames and Panels

There are two main ways to do graphics in Java: Applets and Frames.

Applets are meant to be run from a browser.

Frames can be used from applications as well as applets.

Below is a simple program that creates a frame.

public class SimpleFrameApplication {
   public static void main(String[] args) {
      JFrame window = new JFrame("Frame with nothing in it");
      window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      window.pack();
      window.setVisible(true);
   }
}
Make a new case project, gcs02 in package frames.
Make a new class called SimpleFrameApplication that contains the above program and run it.
What happens and why?

To use a frame, you must put objects in it.
One of the objects that is available is the JPanel which you can (among other things) draw into.

You make your own panels by extending JPanel.
Here is an example:

public class MyPanel extends JPanel {

    public MyPanel() {
       super();
       setPreferredSize(new Dimension(300,400));
    }

    public void paintComponent(Graphics g) {
       g.setColor(Color.black);
       g.drawString("This is my panel", 15,15);
       g.drawRect(40,40,100,50);
   }
}
We can put this panel into our frame by adding the line:
window.add(new MyPanel());

It is possible to have the same program be able to run as either an application or an applet.
Here is a main program that does both without much extra work:

public class SimpleFrameProgram  extends JApplet {
   public static void main(String[] args) {
      JFrame window = new JFrame("Frame with nothing in it");
      window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      window.add(new MyPanel());
      window.pack();
      window.setVisible(true);
   }

   public void init() {
      setSize(300,400);
      add(new MyPanel());
   }

}
Since all of the work is done in MyPanel there is very little duplicated code.

Let's import the Rectangle and DrawableRectangle from our previous case study.

Right click on frames under src in gcs02
(or highlight frames and click on File).
     Import -> General -> File System -> Next
Browse the From Directory and find the applet directory of your gcs01 under src.
Click OK and check the two file you want to import.
Make sure the Into folder is gsc02/src/frames.
Click Finish.
In each of these, change the package name to frames.
Run the program again and make sure it has no errors.

Have the constructor of MyPanel create an array of 10 DrawableRectangles and have the paint method draw them.

What is the property of a DrawableRectangle that makes it drawable?

In other words, what does paint need to know about an object to be able to draw it?

Answer: it needs to have a draw methods.

Lets make an interface that describes this method:

public interface DrawableShape {
   public void draw(Graphics g);
}
and have the DrawableRectangle implement this.

Let us also make another DrawableShape

   public class DrawableCircle implements DrawableShape {

       private int x;
       private int y;
       private int radius;
       private Color color;

       public DrawableCircle(int x, int y, int radius, Color color) {
          this.x = x;
          this.y = y;
          this.radius = radius;
          this.color = color;
       }

       public void draw(Graphics g) {
          g.setColor(color);
          g.fillOval(x-radius,y-radius,2*radius,2*radius);
       }
}
We can now create some of these and have our panel draw them.

How would you put these in the same array as the rectangles?
Answer: make it an array of DrawableShape.

What if we wanted to sort our shapes by size?

What properties would our shapes need to have in order to compare them?
Suppose each drawable shape had a getArea method?
Add this to the DrawableShape interface.
DrawableRectangle already has a getArea() method.
We will have to write one for DrawableCircle.

We could now write a sort method (if we know how) that would sort DrawableShapes by their area.

Java already has a way to sort arrays, but those arrays must implement Comparable.
We can make our DrawableShape also implement Comparable by having the DrawableShape extend Comparable and writing compareTo methods for the DrawableShape instances.

Nested Panels and Labels

So far, we have put a single panel in our frame or applet, and we have drawn in that panel.

A JPanel is also a container that can be used to hold other graphical objects such as other panels, labels, or buttons.

We will start by adding a few labels to MyPanel. The coordinate system used by MyPanel overlaps with the two labels added and the positions of the labels is determined by the size of the labels and the size of the window.

We can do better by making a new panel which will hold the labels and MyPanel.