Java Article 14 - Inheritance

Another interesting and useful feature in object oriented programming(OOP) is inheritance. I talked about inheritance and subclassing a bit back in Article 8. In this article we expand on inheritance a bit and provide a simple example with classes we write ourselves. I am using the example of a game again, however this is not designed to be a lesson in game design, rather it provides examples of sublclassing, inheritance, and overriding methods that many programmers can relate to easily.

In this example we are writing a part of a hypothetical computer Role Playing Game (cRPG). RPGs have been around since the mainframe days and were sold as retail software packages shortly after the birth of the personal computer. Akalabeth: World of Doom, was one of the first computer cRPGs and was created by a teenager named Richard Garriott in 1979.

Much like Bill Gates or Steve Jobs, Richard Garriott was there at the birth of his industry. He is an iconic figure to many having founded Origin Systems, published the great Ultima series of RPGs as well led the development of online games Ultima Online, City of Heroes, City of Villains, and Tabula Rasa. As evidenced by World of Warcraft's > 11 million copies sold, RPGs are still popular subject for games today.

So in our hypothetical example we are tasked with programming the monsters in a RPG. Much like a family tree or a company's organizational chart, monster types can fit into hierarchical structure wherein some types of monsters can be thought of as children of, or subordinates to, others monster types.

In simple terms you could have a Monster class which has variables and methods that define a monster in general terms, then you could define subclasses of Monster which represent specific types or species of monsters such as a zombies or a giant snakes. The Monster subclasses would inherit some of their behaviors and states from the Monster parent class (the superclass) but they would also have differences such as their appearance, their personality, and the damage they do.

So through OOP we can create a class hierarchy and subclass our different monsters from the Monster Class and save ourselves a lot of work. In today's example we will have:

  • A main class that simulates our game engine.
  • A Monster class.
  • Two sublcasses of Monster: Zombie and GiantSnake.
  • Then we will even have a subclass of GiantSnake called PoisonousGiantSnake.

A diagram of these classes would be as follows:

When you subclass you essentially make a copy of the superclass. It is then up to you to modify the new subclass to fine-tune its state and behavior to suit your needs. You modify a subclass by changing the methods and variables that it inherits from its superclass. Changing a method in a subclass is called overriding a method.

Another way creating sublcalsses saves you work is that if you change a method in the superclass, that change is automatically passed on to (inherited) in its subclasses.

Now… we will need a Java file for each of our classes as follows:

Our main class:

public class MainClass 
{

	
	 // Nothing new here. We instantiate 3 objects
	 // and tell them to do stuff.
	 
	public static void main(String[] args) 
	{
			
		Zombie zombie = new Zombie("Bob the Zombie");
		zombie.findAPlayer();
		zombie.attack();
		zombie.speak();
		
		System.out.println();
		
		GiantSnake snake = new GiantSnake("Larry the Giant Snake");
		snake.findAPlayer();
		snake.attack();
		snake.speak();
		
		System.out.println();
		
		PoisonousGiantSnake poisonSnake 
		=new PoisonousGiantSnake("Hank the Poisonous Giant Snake");
		poisonSnake.findAPlayer();
		poisonSnake.attack();
		poisonSnake.poisionPlayer();
		poisonSnake.speak();
		
		
	}

}

Next we add the monster superclass:

public class Monster 
{

	int damage;
	String name;
	
	public Monster(String name)
	{
		this.name = name;
	}
	
	
	 // None of the subclasses of Monster will override the
	 // findAPlayer() method. We can optionally 
	 // add the keyword "final" as a technique to prevent 
	 // other programmers from overriding this method in a subclass.
	
	public final void findAPlayer()
	{
		System.out.println(name + " looks around for " +
				"somone to attack.");	
	}
	
	
	 // All monsters advance before attacking, so we add this 
	 // here then override it in each subclass to add more behavior.
	
	public void attack()
	{
		System.out.println(name + " advances towards you!");	
	}
	
	
	 // This is an example of a design decision. Here I believe 
	 // most monsters will make the sound "Grrrr!" so i put it in
	 // this superclass so it gets inherited in the subclasses.
	 // This saves me some typing. I do have to override this in
	 // the GiantSnake class because snakes hiss instead.
	 
	public void speak()
	{
		System.out.println(name + " says \"Grrrr!\"");
	}
	
}

Now we add our three subclasses:

//Zombie is a subclass of Monster
//or you can think of Zombie "extending" Monster
public class Zombie extends Monster
{
	public Zombie(String name)
	{
		
		 // Monster's Constructor is expecting a String, so we 
		 // have to pass string up to the superclass or we get
		 // an error.
		 // super(name); translates to Monster(name) which is the
		 // name of and a call to Monster's constructor method.
		 
		super(name);
		
		
		 // The int damage is inherited from the superclass, Monster.
		 // We don't have to enter it in again it in this subclass.
		 // Neat shortcut huh?
		 
		damage = 2;
	}

	
	 // Notice Zombie does not have a speak method but 
	 // it still speaks when our MainClass tells it to?
	 // 
	 // This happens because Zombie inherits speak() from
	 // the Monster class. With inheritance we saved ourselves 
	 // some work.
	 
	
	
	 // Here we override the monster classes attack() method since
	 // We want to add an attack message with damage.
	 
	
	
	 // @Override is what is called an annotation. It is an
	 // optional technique we can use to make the compiler issue a
	 // warning if we spell a method that is to be overridden wrong.
	 // 
	 // Misspelled methods that were meant to be overwritten
	 // is a common error. 
	 
	@Override
	public void attack()
	{
		
		
		
		
		 //This tells the monster class to execute it's attack
		 // method then return here and finish executing this
		 // overridden method.
		 
		super.attack();
		System.out.println(name + " claws YOU for " 
				+ damage + " points of damage.");	
	}
	
}

GiantSnake:

public class GiantSnake extends Monster 
{
	public GiantSnake(String name)
	{
		super(name);
		damage = 5;
	}
	
	 // Since snakes don't say "Grrrr!" so we override the 
	 // monster classes speak() method, to make the snake
	 // hiss instead.
	 
	public void speak()
	{
		System.out.println(name + " says \"Hisssss!\"");
	}
	
	public void attack()
	{
		super.attack();
		System.out.println(name + " bites YOU for " 
				+ damage + " points of damage.");	
	}
	
}

PoisonousGiantSnake:

//Here we subclass GiantSnake instead of Monster
public class PoisonousGiantSnake extends GiantSnake 
{
	public PoisonousGiantSnake(String name)
	{
		super(name);
	}
	
	 
	 // speak() and attack() are the same as GiantSnake so we don't
	 // override those methods, we just inherit what we already 
	 // wrote in GiantSnake. PoisonousGiantSnake does have a new
	 // behavior which is to poison the player. 
	 // Below we add a new method poisionPlayer().
	 
	
	public void poisionPlayer()
	{
		System.out.println("You are poisened! You take an additional 2 points of damage!");
	}
	
}

Do you see how handy sublcassing is? It allows you re-use your code in a new way. You can do the same thing with any class, such as creating your own custom form element such as a JButton. You also subclass JFrame when you create your own Swing interface.

Notice how findAPlayer() is inherited and unaltered (not overridden) in all of the sublclasses? Suppose your boss comes to you in a panic saying "OMG! Marketing says the wording in our game is not aggressive enough! They are threatening to have me fired!"

"Relax." You reply, "All I have to do is change one line of code and the behavior in all of the monsters will change because I used subclasses."

So in our example code we could change the wording in the Monster class to make it more "aggressive" and save our boss's job.

public final void findAPlayer()
{
	System.out.println(name + " looks around for someone to kill.");
}

Also consider in a RPG there may be hundreds or thousands of monster types. Imagine if you did not use sublcasses: You would have to make this change to thousands of classes like GiantSnake, Zombie and GiantKillerPenguin, which adds up to a lot of labor. This type of labor typically falls into the "repetitive and not very fun" class of labor too.

Now, a word of warning: subclassing to modify behaviors is not always the best way to manage behaviors. In larger more complex applications it is sometimes better to use a strategy design pattern. Design patterns can save you even more work and will make modifying and updating your programs even easier. Design patterns are a more advanced topic which I will cover later. I mention this now because most Java textbooks don't mention that there are drawbacks to inheritance and subclassing.

Don't worry about design patterns for now but just keep in mind there may be a better way to structure your program. Do some research before you design you program because many of the problems you have to solve have already been solved, and these solutions have been refined and optimized by many people over the span of many years.