Inheritance

The big idea

Inheritance is a yet another form of abstraction.

(Yeah, we only have one big idea in computer science.)

Abstraction review

Variables

Variables abstract values.

When we define a variable we are defining the idea of how a whole set of values can be used.

int speed; // The speed something is moving.
String name; // Something's name.

At different times the variable will hold different values but in an abstract sense it will always represent the same thing.

Methods

Methods abstract computations.

public int distance(Point a, Point b) {
  return Math.hypot(a.x - b.x, a.y - b.y);
}

Each call to a given method with different arguments does something different.

But the differences have been abstracted away so we can talk about “what the method does”.

Classes

Classes abstract a set of computations over related values.

public class Student {
  private String name;
  private int grade;
  private ArrayList<Course> courses;

  public double gpa() { ... }
  public int creditsEarned() { ... }
  // etc.
}

Every instance of a class has its own state but all instances have the same set of methods, abstractly defining what we can do with those instances.

Inheritance

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.

Mechanics

extends

public class GridGame extends JPanel {}

public class TicTacToe extends GridGame {}

This establishes an is-a relationship, as in tic tac toe is a grid game.

GridGame is called the “parent class” or “super class”.

TicTacToe is called the “child class” or “subclass”.

(GridGame also has a parent class, namely JPanel.)

No extends

Any class without an explicit extends clause implicitly extends java.lang.Object.

This is why every class has a toString and equals method even if we don’t write them.

Constructors

Recall that the role of constructors is to make sure an instance is property initialized and ready to be used.

A GridGame has to have its rows, columns, and padding initialized so it has a constructor that takes those as arguments.

public GridGame(int rows, int columns, int padding) {
  this.rows = rows;
  // etc.
}

super

Since a TicTacToe is a GridGame it also must initialize those things.

We can use super to invoke the code of the GridGame constructor.

public TicTacToe() {
  super(3, 3, 4);
  // rest of constructor
}

Inherited variables

Variables are inherited but if they are private they are still not directly accessible in the subclass.

But every instance of the subclass does have those variables and they will be used by methods inherited from the super class.

Inherited methods

Methods are inherited which means any method you can call on an instance of the super class you can also call on an instance of the subclass.

Adding variables and methods

The subclass can define its own variables and methods that don’t make sense in the super class.

public class TicTacToe extends GridGame {

  private int move = 0;

  public boolean isDraw() { return move == 9; }

}

Overriding methods

This is the real power move of inheritance. 💪

A subclass can define a method that has the same signature as an existing method from the super class.

This allows the behavior of different classes to differ based on the class of the object they are invoked on, not just on the arguments.

Polymorphism

This goes by the fancy Greek-derived word “polymorphism”, or “many forms”.

As in, the same method in different classes can take on many forms.

toString

Every class in Java has a toString method.

Many classes override toString to provide a more useful human-readable representation of instances of the class.

Object o = new Object();
Object s = "foo bar";
Object n = Integer.valueOf(10);

o.toString() ⟹ "java.lang.Object@4b9af9a9"
s.toString() ⟹ "foo bar"
n.toString() ⟹ "10"

Liskov substitution principle

Defined by Barbara Liskov, MIT professor and 2008 Turing Award Winner.

While different classes have different behavior (that’s the whole point), subclass’s behavior should be enough the same that you can substitute an instance of the subclass anywhere you can use an instance of the super class.

Exactly what the same means is a big part of defining your abstraction.

🚨 Inheritance is not about code resuse! 🚨

While extending a class means you get some methods for free you should never extend a class unless there is an actual “is-a” relationship between the subclass and the super class.