Return to lecture notes index

15-100 Lecture 15 (Friday, October 6, 2006)

Today's Quiz

  //1. Please implement a class that achieves the following:


  //A. Models a Rectangle
  class Rectangle {
  
    // B. Maintains its length and width
    private double length;
    private double width;
   
   
   //C. Requires the length and width to be set when the object is created
   // This is called a constructor
   public Rectangle (double length, double width) {
     this.length = length;
     this.width = width;
   }
  
  
    // D. Provides methods to get each of the length, the width and the area
    //These are typically called getters
    public double getLength() {
      return length;
    }
    
    
    public double getWidth() {
      return width;
    }
    
    
    public double getArea() {
      return (length * width);
    }
  }
  
   public static void methodXYZ() {
    Rectangle rect = new Rectangle (1, 2);
    
    double area = rect.getArea();
  }

  
To String
There's one more method that will make our triangle complete and help a lot with testing, the toString() method. The toString() method gets called on everytime we say something like System.out.println(foo). If foo was an object, it would call the toString() method for that object. If we were to NOT write this toString() method within our Rectangle class, the Java compiler would successfully compile and run our code. The reason for this is that all classes in Java are assumed to extend the Object class, which is built into Java's API (the library of classes and methods that comes standard with Java and that the compiler automatically knows about). In brief, because our class is implicitly an extension of Object, it inherits a working method called toString().

However, because we have not specialized the toString() method to fit the details of our own Rectangle class, the toString() method will not print anything useful (it will print a barely intelligible collection of characters and numbers--which is the defination of the toString() method that it inherited). We can override the toString() method in Object by adding our own toString() method to our Rectangle class. The compiler will know to use ours instead of the inherited toString() method.



       public String toString() {
              return length + " x " + width;
       }
  
More About Inheritence

So what do you think of when you hear the word inheritance? Money that you receive from your parents as a birthright, maybe your genetic code. Inheritance exists in Java too.

When a class is designed as a subtype of another class, by "extending" the "base" or "parent" class, it inherits all of the parent class's methods.

All classes are a subclass of the class Object. This provides a uniform interface across all Objects and thus all classes. When you create a class it automatically inherits several methods from the object class.

Let's look at two methods in Object:


  class Object{
     public String toString(){
       //magic performed here
     }

     public boolean equals(Object o){
      //more magic performed here
     }
  }
  

Now these two inherited methods aren't too useful in the grand scheme of things. The inherited equals() performs the == operation until you override the method by writing your own version of it in the subclass you are working on. (Don't remember what == does? Look below for a refresher).

The toString() is equally as useful. The inherited toString prints out the name of the subclass and then a series of numbers and letters which tell where the reference variable, holding the instance of the class you want to go toString(), is pointing (aka where the object is being stored in the computer's memory). More commonly, it is appropriate for the toString to provided critical information about our object's state. (This usually requires the method to return a String containing most of the information that the object's instance variables are holding.)

== and .equals()

There's a big difference between == and equals(). A programmer needs to know which comparison to use and when. The == always does the same job. It looks at the values that two variables are holding and returns a boolean saying whether the two values are equal. This works well enough for the primitive types (such as int, double, char, long, etc.) since a variable for a primitive holds the primitive's value. However when you start using the == operator with Objects (such as Strings) you need to remember that a variable "holding" a String doesn't actually hold the String's value. Instead they hold a reference to the String and where it is in the computer's memory.

For this reason the line ("Hi"=="Hi"); would evaluate to false. That line makes two instances of a String that says "Hi". Even thought the state of the Strings are the same the instances are different and thus == will say that these two Strings aren't the same String.

So how do you compare two Strings to see if they have the same state or same words "inside" of them? That is what the .equals() is for. It looks at the Object's state and says whether the two states are the same.

Since the inherited .equals() for Objects uses the == comparison you can see why it is useful to always redefine this inherited method.