Inheritance and polymorphism in p5JS

You may have encountered the terms inheritance and polymorphism in your programming life before this book. After all, they are two of the three fundamental principles behind the theory of object-oriented programming (the other being encapsulation). This is covered in other Processing or Java programming books too. The text, Learning Processing, has close to an entire chapter (#22) dedicated to these two topics.

Inheritance basics.

Let’s take a different example, the world of animals: dogs, cats, monkeys, pandas, wombats, and sea nettles. We’ll start by programming a Dog class. A Dog object will have an age variable (an integer), as well as eat(), sleep(), and bark() functions.

class Dog {

   contructor() {  
    this.age = 0;  
   } 
 
   eat() {  
    println("Yum!");  
   }  

   sleep() {  
   println("Zzzzzz");  
   }

   bark() {  
    println("WOOF!");  
   }
  }

Dogs and cats have the same variables (age) and functions (eat, sleep).
A unique function for barking.

Now, let’s move on to cats.

class Cat {

  constructor() {
    this.age = 0;
  }
 
  eat() {
    println("Yum!");
  }
 
  sleep() {
    println("Zzzzzz");
  }
 
  meow() {
    println("MEOW!");
  }
}

As we rewrite the same code for fish, horses, koalas, and lemurs, this process will become rather tedious. Instead, let’s develop a generic Animal class that can describe any type of animal. All animals eat and sleep, after all. We could then say:

  • A dog is an animal and has all the properties of animals and can do all the things animals do. Also, a dog can bark.
  • A cat is an animal and has all the properties of animals and can do all the things animals do. Also, a cat can meow.

Inheritance makes this all possible. With inheritance, classes can inherit properties (variables) and functionality (methods) from other classes. A Dog class is a child (subclass) of an Animal class. Children will automatically inherit all variables and functions from the parent (superclass), but can also include functions and variables not found in the parent. Like a phylogenetic “tree of life,” inheritance follows a tree structure. Dogs inherit from canines, which inherit from mammals, which inherit from animals, etc.

Nature of Code Image

Figure 4.2

Here is how the syntax works with inheritance.

The Animal class is the parent (or super) class.
//animal class is the parent class
 
class Animal { 
 
//Dog and Cat inherit the variable age.
 
  constructor() {
    this.age = 0; 
  }
 
//Dog and Cat inherit the functions eat() and sleep().
 
  eat() {
    println("Yum!");
  }
 
  sleep() {
    println("Zzzzzz");
  }
}
 
 
//The Dog class is the child (or sub) class, indicated by the code "extends Animal".
 
class Dog extends Animal {
  constructor() {
    super(); //super() executes code found in the parent class.
  }
 
//We define bark() in the child class, since it isn't part of the parent class.
  bark() {
    println("WOOF!");
  }
}
 
class Cat extends Animal {
  constructor() {
    super();
  }
 
  meow() {
    println("MEOW!");
  }
}
This brings up two new terms:
  • extends – This keyword is used to indicate a parent for the class being defined. Note that classes can only extend one class. However, classes can extend classes that extend other classes, i.e. Dog extends Animal, Terrier extends Dog. Everything is inherited all the way down the line.
  • super() – This calls the constructor in the parent class. In other words, whatever you do in the parent constructor, do so in the child constructor as well. Other code can be written into the constructor in addition to super(). super() can also receive arguments if there is a parent constructor defined with matching arguments.

A subclass can be expanded to include additional functions and properties beyond what is contained in the superclass. For example, let’s assume that a Dog object has a haircolor variable in addition to age, which is set randomly in the constructor. The class would now look like this:

//A child class can introduce new variables not included in the parent.
class Dog extends Animal { 
  constructor() {
    super();
    this.haircolor = color(random(255));
  }
 
  bark() {
    println("WOOF!");
  }
}

Note how the parent constructor is called via super(), which sets the age to 0, but the haircolor is set inside the Dog constructor itself. If a Dog object eats differently than a generic Animal object, parent functions can be overridden by rewriting the function inside the subclass.

class Dog extends Animal {
 
  constructor() {
     super();
     this.haircolor = color(random(255));
  }
 
 
//A child can override a parent function if necessary.
 
  eat() {
 
//A Dog's specific eating characteristics
 
    println("Woof! Woof! Slurp.")
  }
 
  bark() {
    println("WOOF!");
  }
}

 

But what if a dog eats the same way as a generic animal, just with some extra functionality? A subclass can both run the code from a parent class and incorporate custom code.

class Dog extends Animal {
 
   constructor() {
     super();
     this.haircolor = color(random(255));
   }
 
   eat() {
 
//Call eat() from Animal. A child can execute a function from the parent 
//while adding its own code.
 
     super.eat();
 
//Add some additional code for a Dog's specific eating characteristics.
 
     println("Woof!!!");
   }
 
   bark() {
    println("WOOF!");
  }
}

Finally, here is the example where the parent constructor requires an argument.

class Animal { 
  constructor(name) {
    this.name = name;
  }
 
  speak() {
    print(this.name + ' makes a noise.');
  }
}
 
class Dog extends Animal {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }
 
  speak() {
    print(this.name + ' barks.');
  }
}
 
//In your program call the class to make a dog object d.
//Then call its functions!
 
var d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

Tutorial is taken from Nature of Code by Daniel Shiffman (https://natureofcode.com/)