Processing tutorial: inheritance and abstract classes

Recently I’ve been using Processing for a number of graphics programming projects. There are a lot of web tutorials covering concepts like inheritance, interfaces, inner classes, polymorphism, and so on from a Java perspective, but very few bridge the gap between Java and Processing, and fewer still offer working Processing demonstrations. Further, a great number of Processing tutorials and demos seem to focus on specific animation techniques and not on writing solid, maintable code following best practices. I’m writing a series of tutorials for the advanced beginner or intermediate level Processing user interested in taking their programming skill to the next level.

Prerequisite to understanding this tutorial is having a decent sense of object-oriented programming, specifically how to define and use classes. This tutorial at the Processing website is a good start.

Here’s a common problem for Processing programmers. We have a bunch of objects, lets call them creatures, which are similar in some fundamental ways, but have differences which are not simply superficial. All of the creatures have a shared vocabulary of properties and methods: they all have a width and a height, and they all can be drawn to the screen. However, each creature has one or two actions which are unique. For example, one type of creature might search the screen for food to eat, another might change colors when clicked, and a third might be capable of spontaneous asexual reproduction.

The breadth of difference in functionality might initially suggest coding each creature as a different class, like this:

class Monkey { int w, h; Monkey(initWidth, initHeight) { [...] } void draw() { [...] } void lookForFood() { [...] } } class Chameleon { int w, h; Chameleon(initWidth, initHeight) { [...] } void draw() { [...] } void changeColors() { [...] } } class Amoeba { int w, h; Amoeba(initWidth, initHeight) { [...] } void draw() { [...] } void reproduceAsexually() { [...] } }

This is bad for two reasons. First, as Good Programmers, it offends our sensibilities by violating a cardinal rule of our craft: Don’t Repeat Yourself. We are specifying the properties of width and height as well as the constructor and draw() functions three times with no structural variation, so this is two times too many. Second, we’d like to keep track of all our creatures in one big ArrayList. While we’re free to add Monkeys, Chameleons, and Amoebae to an ArrayList, things go wrong when we try to take them out. Processing and Java will require us to cast the object we are getting from the list to a specific type. For example, we might want to do something like this:

for(int i = 0; i < creatureList.size(); i++) { currentCreature = (Monkey) creatureList.get(i); currentCreature.draw(); }

But what if the current creature is not, in fact, a Monkey, but instead an Amoeba? Then we’ll get a type error. We could keep separate lists of Monkeys, Chameleons, and Amoebae—but then we’d be repeating ourselves. And what if we later wanted to add GiantSloths or Tardigrades? Our code would be difficult to modify and maintain. Fortunately, we have a few tools which can help us out. The tool we’ll talk about today is the abstract class, which allows use of something called inheritance.

We can specify an abstract class, called Creature, which defines the properties and methods common to every object deigning to call itself creature. We can then implement subclasses of Creature (e.g. Monkey, Chameleon, and Amoeba) which have additional functionality of their own.

abstract class Creature { int w, h; Creature(int initWidth, int initHeight) { w = initWidth; h = initHeight; } abstract void draw(); } class Monkey extends Creature { Monkey(int initWidth, int initHeight) { super(initWidth, initHeight); } void draw() { [Draw a monkey.] } } class Chameleon extends Creature { Chameleon(int initWidth, int initHeight) { super(initWidth, initHeight); } void draw() { [Draw a chameleon.] } }

An abstract class is like a partially filled-in template for subclasses. Members of an abstract class can never be directly instantiated (that is, created directly). Given the code above, for example, it would make no sense to write something like Creature aCreature = new Creature(2,3);. Instances of classes like Monkey and Chameleon can be Creatures, but there’s no such thing as an object which is just a Creature and nothing else. Every subclass of Creature inherits its methods and variables. In this case, that means that Monkeys and Chameleons have integers for storing their width and height.

Abstract classes also make demands of their subclasses. The code abstract void draw(); means that every subclass of Creature is required to implement a function of type void called draw(). If a subclass didn’t implement this draw() function, we’d get an error. This set of requirements is like a contract that each subclass must honor. I’m going to expand on this notion of contractual obligation in the next tutorial, which will include a section on another tool we could use for this purpose: interfaces.

Additionally, something funny is happening in those subclass constructors. Subclasses are allowed to call the constructor function of their superclass, in this case Creature(), by using the method super(). The Monkey() and Chameleon() constructors just pass along the initial width and height values to the superclass constructor which handles the value assignment. If a subclass has additional variables not common to the superclass, those should also be initialized in the constructor. For example, Chameleons might want to store their current color in a variable, and begin life with a color passed to the constructor. We’d modify the Chameleon subclass as follows:

class Chameleon extends Creature { int c; Chameleon(int initWidth, int initHeight, int initColor) { super(initWidth, initHeight); c = initColor; } void draw() { [Draw a chameleon.] } }

Now we can populate an ArrayList with Monkeys and Chameleons, then iterate through that list, casting each item to the type Creature. Because the Creature superclass requires all its subclasses implement the draw() method, we can safely call this method on any Creature we get from the ArrayList. Here’s how that looks:

ArrayList creatureList = new ArrayList(); Monkey hanuman = new Monkey(4,8); Chameleon lizzy = new Chameleon(7,3); creatureList.add(hanuman); creatureList.add(lizzy); for(int i = 0; i < creatureList.size(); i++) { currentCreature = (Creature) creatureList.get(i); currentCreature.draw(); }

Mighty convenient. Now if we add additional variables or methods to the creature class, all of its subclasses inherit that functionality while preserving their unique features. Below is a complete working demonstration of these principles in action, using Ellipsoids and Rectithings, creatures which are much easier to draw than apes and lizards.

Click on the sketch to give it focus, then press the spacebar to move all the creatures to the center of the screen.

This browser does not have a Java Plug-in.
Get the latest Java Plug-in here.

Source code: abstractClasses

// Create three creatures. Ellipsoid jane = new Ellipsoid(100, 50, 300, 150); Rectithing alan = new Rectithing(40, 15, 100, 50); Ellipsoid bob = new Ellipsoid(10, 70, 150, 200); // Create our ArrayList for creature storage. ArrayList creatureList = new ArrayList(); void setup() { size(400,300); frameRate(30); // Add our creatures to the list. creatureList.add(jane); creatureList.add(alan); creatureList.add(bob); } void draw() { background(222); // Iterate through the list of creatures, telling each one to draw // and move. for(int i = 0; i < creatureList.size(); i++) { Creature currentCreature = (Creature) creatureList.get(i); currentCreature.draw(); currentCreature.move(); } } // When the spacebar is pressed, iterate through the creatureList // and move every creature to the center. void keyReleased() { if(key == ' ') { for(int i = 0; i < creatureList.size(); i++) { Creature currentCreature = (Creature) creatureList.get(i); currentCreature.moveToCenter(); } } } abstract class Creature { // Every subclass will inherit these variables. int w, h, x, y; Creature(int initWidth, int initHeight, int initX, int initY) { w = initWidth; h = initHeight; x = initX; y = initY; } // Every sublcass will inherit this method. void moveToCenter() { x = int(width * 0.5); y = int(height * 0.5); } // Every subclass is required to implement these methods on their own. abstract void draw(); abstract void move(); } class Ellipsoid extends Creature { // Ellipsoids have a changing color. int c; Ellipsoid(int initWidth, int initHeight, int initX, int initY) { // Call the superclass constructor on initialization. super(initWidth, initHeight, initX, initY); c = int(random(255)); } void draw() { fill(c); ellipse(x,y,w,h); } void move() { x += int(random(-2,2)); y += int(random(-2,2)); w += int(random(-3,3)); h += int(random(-3,3)); c += int(random(-10,10)); } } class Rectithing extends Creature { Rectithing(int initWidth, int initHeight, int initX, int initY) { super(initWidth, initHeight, initX, initY); } void draw() { fill(127); rect(x,y,w,h); } void move() { x += int(random(-3, 3)); y += int(random(-3, 3)); } }

3 Responses to “Processing tutorial: inheritance and abstract classes”

  1. PenguinTutor Says:

    Thanks for this. I’m trying to move over to more structured OO programming. Processing makes this very easy, but the tutorials provided (and books available) don’t go into OO programming enough to cover abstract classes etc. I’ve been searching for an article that goes that little bit further than the standard tutorials on Processing and this does just the job.

  2. Beau Sievers Says:

    Hey, thanks. Good to hear this has been of use to someone. Some day I will finish this series, maybe.

  3. Loan Myers Says:

    Hi Beau,

    thanks for the tutorial. This is the answer I’ve been looking for all day!

    Cheers. And I’m going to bookmark your site!

Leave a Reply