Inherited methods

Recall from previous slides

Inheritance allows us to abstract over a set of classes that have similar structure and behavior.

Example

public class Shape {
  public void draw(Graphics g) { ... }
  public double area() { ... }
  public Point centroid() { ... }
  public double distanceTo(Shape other) { ... }
}

public class Circle extends Shape { ... }
public class Triangle extends Shape { ... }
public class Rectangle extends Shape { ... }

Shape in an abstraction for a number of different kinds of shapes.

Overriding methods

When a subclass defines a method with the same signature as one defined in its superclass.

When that method is invoked on an instance of the subclass, the method defined in the subclass is run rather than the one in the superclass.

When methods take on many forms, what is the fancy word for it?

Polymorphism, Greek for “many forms”.

Two reasons to override a method

  • To augment the behavior of the superclass.

  • To fill in behavior of a method in a specific way.

Augmenting behavior

We want to augment behavior when the superclass has useful behavior but we also want to do something else.

Remember the Liskov substitution principle

Instances of a class need to live up to the contract of the superclass so they can be substituted in anywhere an instance of the superclass could be used.

Some of this is guaranteed by the compiler—the return types have to match, etc.

But other bits are up to you.

super for methods

To properly augment behavior, we may need to invoke the superclass version of the same method.

The keyword super allows us to do that.

public void foo() {
  super.foo();
  doSomethingElse();
}

super is kind of like this, a magic variable name that refers to the current object but only for the purpose of invoking a method from the superclass.

A superclass

public class Student {

  // fields and constructors here

  public String toString() {
    return "Name: " + name + "; Grade: " + grade;
  }
}

A subclass

public class SLCStudent extends Student {

  // new fields and constructors here

  public String toString() {
    return super.toString() + "; SLC: " + slc;
  }
}

Filling in behavior

public class Shape {
  public void draw(Graphics2D g) {
    // do nothing.
  }
}

All Shapes can be drawn but there’s no way to draw a completely generic shape so this method does nothing.

Filling in behavior, subclass

But in subclasses we can define meaningful draw methods.

public class Circle extends Shape {
  public void draw(Graphics2D g) {
    g.drawOval(x, y, radius, radius);
  }
}

public class Rectangle extends Shape {
  public void draw(Graphics2D g) {
    g.drawRect(x, y, width, height);
  }
}

Beyond the AP

Often, as here, no useful implementation of a method in the superclass.

This is because Shape is just an abstraction—it represents the idea of shapes and what you can do with them but we can only draw actual, concrete shapes.

Abstract classes (not on AP)

We can declare classes and methods abstract and then the compiler will make sure that all subclasses actually implement the necessary methods.

public abstract class Shape {

  public abstract void draw(Graphics2D g);

  public abstract double area();

}

More realistic

public abstract class Shape {

  public abstract void draw(Graphics2D g);

  public abstract double area();

  public abstract Point centroid();

  public double distanceTo(Shape other) {
    Point me = centroid();
    Point them = other.centroid();
    return Math.hypot(me.x - them.x, me.y - them.y);
  }
}

Even more beyond the AP

Interfaces—like abstract classes but better.

Actually one of the most important features of Java.

To learn about them, sign up for my advanced course.