Class 12

Homework

Written homework - see here.

Inheritance

The main topics of the first six weeks were learning Java and information hiding. The major topic of the second six weeks is equally important: inheritance.

Inheritance is based around the idea of object hierarchies. Oftentimes, we have objects that have a kind-of relationship with other objects. For example, a circle is a kind of a shape, or a kangaroo is a kind of a mammal. Kangaroos can do everything a mammal can do (grow hair, have live young, etc.), but can also do many other kangaroo-specific things (hop, box, etc.). The same is true of Mole Rats, or People; they all share some abilities, while have a few specific ones of their own. If we were writing code using many different kinds of mammals, it would be truly wasteful to continually write the same identical "growHair()" function for each of them, as well as the fields necessary to allow that function to work. Instead, we would create a Mammal class, define all the types of things all mammals can do, and all the types of fields all mammals have, and then allow our Kangaroo, MoleRat, and Person classes to "inherit" those methods and fields.

So, what would this look like?

  public class Polygon {
    int[] sideLengths;
    int numSides;

    public Polygon(int[] lengths){
      sideLengths = lengths;
      numSides = lengths.length;
    }
  
    public int perimeter() {
    //compute perimeter
      int sum = 0;
      for (int i = 0 ; i <  sideLengths.length ;i++) {
        sum += sideLengths[i];
      }
      return sum;
    }

    //No area method.
  }

  public class Triangle extends Polygon {

    public Triangle(int[] lengths) {
      super(lengths);  //Calls superclass constructor
      numSides = 3;
    }
    public Triangle () {
      this(new int[3]);
    }
    //No perimeter needed

    public double area (){
      double s = perimeter()/2;
      //Heron's formula
      return Math.sqrt(s*(s- sideLengths[0])*(s- sideLengths[1])*(s - sideLengths[2]));
    }
  }

Sometimes a parent class implements a method but we might want to do the same thing but differently for the child. In this case, it is possible to override the parent's method:

  public class Rectangle extends Polygon {
    public double area() {
      return sideLengths[0]*sideLengths[1];
    }
  }

  public class Square extends Rectangle {
    public double area() {
      return sideLengths[0] * sideLengths[0];
    }
  }
  

In this case we say that the area in Square overrides the area in rectangle.:

  public class Square extends Rectangle { 
    public double area() { 
      return sideLengths[0] * sideLengths[0]; 
    } 
  } 
  

Much of the time, we make subclasses when we have a special case of a class, just like above. But another use for extending a class is to add functionality to the superclass. Suppose somebody has given us a Word class, and we want to make a linked list out of it. We could make a class called Node that has fields of Word and Node, OR we could extend Word, and add the field:

  public class WordNode extends Word {
    public WordNode next;
  }
  

Why would we want to do this instead of just making a class that contained the Word? Because we get all sorts of other methods for free, such as the getQueued() method. Otherwise we would have to write it in order to use our new class.

The disadvantage of this is clarity; if String is a member field, it is made explicit what is being represented. By extending, we have data fields defined in multiple places.

So, then, how do we decide when to use extending for enhancement, and when to use fields? There are no hard and fast rules, but in general the closer the new class is to an existing class, and the more you want to use already written functions, the more you want to extend the class.

Many OOP languages allow a class to extend multiple superclasses, but Java only allows one. All classes descend from the base class Object. If you don't say 'extends' your class automatically extends Object. This gives several advantages we'll talk about later, but one is that they can put some methods in Object directly. toString() is one such method. The toString() in Object converts the pointer address to a string.

Whenever you write toString() or any of the other methods of Object, you are overriding that method.