Intro to Java Abstract Classes

This entry is part 5 of 13 in the series Intro to Java

Abstract classes are an interesting piece of Java. They can’t be used on their own, and they have a unique place in our code. They are often confused with interfaces, but they provide a great way to prevent code duplication and ensure consistent structures when building an application.

 In this tutorial we want to look at what Abstract classes are and when to use them. We will take a look at the previous Interfaces lesson and see how we could refactor our code from that lesson to use an Abstract class.

Sun Trails

Index: http://java.sun.com/docs/books/tutorial/index.html

Link to abstract section: http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html 

What is an Abstract class?

An abstract class can be thought of as taking the parts of a concrete class and an interface and combining them.

  • Like an interface, we can define contract methods that must be implemented by extending classes.
  • Like an interface, we can not create a “new” object from our abstract class.
  • Like a class, we can have variables that are not final or static.
  • Like a class, we can have methods.

 
Here is an example:

 
public abstract class AbstractExample {
   public String getFirstName() {
      return "Chris";
   }

   public abstract String getLastName();
}

We have two different methods here. The first is a normal method like we are used to seeing. The second is an abstract method. This method signature is a lot like the interfaces we looked at. This means that AbstractExample is a partial implementation. We have implemented functionality for getFirstName(), but not for getLastName(). We are requiring other extending classes to do that.

When do we have an abstract class?

  • Any class that uses the modifier “abstract” in the class definition is abstract.
  • If a class has a method that is abstract, then it should be marked as an abstract class.
  • If a class implements an interface, but doesn’t implement all of the methods of the interface, then it can be marked as an abstract class.

Why don’t we just use an interface?

I like to think of interfaces as the contract. This is the public face. The published API. An abstract class can be used this way as well, but I think it muddies the waters between the contract and the implementation. The interface is the contract, and an abstract class is ONE partial implementation of that contract. The power of the abstract class is two things.

  1. Allows you to partially code functionality while forcing specifics down the hierarchy.
  2. Allows you to move common code from the concrete classes up the hierarchy.

Here is an example of #1:

name example
 
Here we have an interface with one method: getName(). This method is implemented by the abstract class, AbstractNameExample. This class, in the getName() method also calls to two other methods to help it do the work. These methods, getFirstName() and getLastName() are left to the class that extends ImplementingNameExample. In this way, you can code certain functionality (getName()), while pushing the specific code (getFirstName() and getLastName()) down.

public interface Name {
	public String getName();
}
public abstract class AbstractNameExample implements Name {

   public String getName() {
      return getLastName() + ", " + getFirstName();
   }

   protected abstract String getFirstName();

   protected abstract String getLastName();
}
public class ImplementingNameExample extends AbstractNameExample {
   protected String getFirstName() {
      return "Chris";
   }

   protected String getLastName() {
      return "Ward";
   }
}

This example might be a little made up, but the concept is there. Our public exposure is through the interface. The abstract class is one implementation of this interface. It determines how the name will be presented. There could be another class that instead of returning “last name, first name” returns “My name is firstName lastName.” This is the implementation. In this case the implementation has decided that it only wants to provide the formatting, not the actual code that retrieves the names. This pushes that responsibility down. Now we could have different classes that extended AbstractNameExample, one hard coded like ImplementingNameExample, maybe one using a data base, or one using a random name generator. None of those classes will worry about the formatting.

Here is an example of #2

This concept is about pulling code up into a “base” class. When we have functionality that is common to several classes, instead of cutting and pasting into each class, we can move it up. Well, instead of me showing you, I want you to think about the examples from the last lesson on interfaces. In that¬† lesson we had an interface, Animal, and three concrete classes: Bird, Fish, and Mammal. What would the diagram look like, and what code could we move into a base class?

Lets look at one of the classes from the interfaces lesson for the Fish, and see what we would do first.

public class Fish implements Animal {

   private static final String UNNAMED = "unnamed";

   private String name;

   public Fish() {
      this(null);
   }

   public Fish(String pName) {
      setName(pName);
      System.out.println("Fish created.");
   }

   public void move() {
      System.out.println(getDescription() + " is swimming.");
   }

   public void speak() {
      System.out.println(getDescription() + " says glub glub.");
   }

   public String getType() {
      return "fish";
   }

   public String getName() {
      return name;
   }

   public void setName(String pName) {
      if (pName == null || pName.length() == 0) {
         name = UNNAMED;
      } else {
         name = pName;
      }
   }

   private String getDescription() {
      StringBuilder aBuilder = new StringBuilder();
      aBuilder.append("A ");
      aBuilder.append(getType());

      if (getName() != null && !getName().equals(UNNAMED)) {
         aBuilder.append(" named ");
         aBuilder.append(getName());
      }
      return aBuilder.toString();
   }
}

¬†If we were to create a class, BaseAnimal that was abstract, and implemented the Animal interface, what would we “push up”? Where would the static variable go? Where would the name variable go? Where would the constructors go? What methods HAVE to be in a Fish class, and what are methods that seem more generic? How about this:

animals1 

So, what have we got? Take a look at one of the concrete classes, like Fish. What is left? Only the specifics that need to be implemented for a fish, and only a fish. We have the constructors, and the move(), speak(), and getType() methods.

public class Fish extends BaseAnimal {
   /**
    * This constructor passes the actual constructing to another constructor in
    * this class.
    */
   public Fish() {
      this(null);
   }

   /**
    * This constructor calls it's parent classes constructor (BaseAnimal) with
    * a name. It must be the first line. It can then add beheviour if it would
    * like.
    *
    * @param pName
    */
   public Fish(String pName) {
      super(pName);
   }

   /**
    * Implements the move() method from the Animal interface
    */
   public void move() {
      System.out.println(getDescription() + " is swimming.");
   }

   /**
    * Implements the speak() method from the Animal interface
    */
   public void speak() {
      System.out.println(getDescription() + " says glub, glub.");
   }

   /**
    * Implements the getType() method from the Animal interface
    */
   public String getType() {
      return "fish";
   }
}

What have we done? We decided that the name variable, and the handling of that name were common to all Animal classes. Therefore, we moved the variable, the constructors, and the getName() method up to the BaseAnimal. We also moved the getDescription() method up. It is an exact copy line for line on each of the classes. A great method to move up.

Are there any abstract methods in this class? Why is marked at abstract then? It doesn’t complete the interface, so we must mark it as abstract so the compiler knows that we know what we are doing.

Notice that this getDescription() method calls the interface method getType(). This works much the same way as when we were describing how an abstract class can “push down” functionality. This time we are not calling an abstract method, but one that is already defined in the interface.
 

Does it work?

Yup, just like the examples from the interfaces lesson. Exactly the same. We haven’t changed any of the functionality, just refactored the existing code to add another object layer in there. What has this done for us? It reduces potential maintenance issues. What if we had wanted to change the “null” name from “unknown” to “who knows”? We would have changed it in three classes. What if we missed one? This will also save us adding of new animals in the future.

See the AnimalsTest1, 2, 3. See AnimalsListTest1,2. They function just like the Interface Tests.

public class AnimalsTest1 {

   public static void main(String[] pArgs) {

      Bird aSparrow = new Bird();
      Mammal aCollie = new Mammal();
      Fish aMinno = new Fish();

      System.out.println("Moving animals:");
      aSparrow.move();
      aCollie.move();
      aMinno.move();

      System.out.println("Getting animals to speak:");
      aSparrow.speak();
      aCollie.speak();
      aMinno.speak();
   }
}
public class AnimalsTest2 {

   public static void main(String[] pArgs) {

      // create a Bird object
      Bird aSparrow = new Bird("Tweety");
      
      // create a Mammal object passing "Rover" to constructor
      // this gives the Mammal a name.
      Mammal aCollie = new Mammal("Rover");
      // rename the Mammal object
      aCollie.setName("Lassy");

      // create a Fish object
      Fish aMinno = new Fish();

      System.out.println("Moving animals:");
      aSparrow.move();
      aCollie.move();
      aMinno.move();

      System.out.println("Getting animals to speak:");
      aSparrow.speak();
      aCollie.speak();
      aMinno.speak();
   }
}
public class AnimalsTest3 {

   public static void main(String[] pArgs) {

      Animal aSparrow = new Bird();
      // Creates a Bird object. As an Animal, only the methods of the Animal
      // interface are available

      Animal aCollie = new Mammal();
      // Creates a Mammal object. As an Animal, only the methods of the Animal
      // interface are available

      Animal aMinno = new Fish();
      // Creates a Fish object. As an Animal, only the methods of the Animal
      // interface are available

      System.out.println("Moving animals:");
      aSparrow.move();// move method on object aSparrow of class Bird
      aCollie.move(); // move method on object aCollie or class Mammal
      aMinno.move(); // move method on object aMinno of class Fish

      System.out.println("Getting animals to speak:");
      aSparrow.speak(); // speak method on object aSparrow, of class Bird
      aCollie.speak(); // speak method on object aCollie, of class Mammal
      aMinno.speak(); // speak method on object aMinno, of class Fish
   }
}
public class AnimalsListTest1 {
   public static void main(String[] pArgs) {

      // creating a list that could contain any object,
      // but we will just put Animals in it
      List animalList = new ArrayList();

      // create each object as an animal to allow us
      // to treat each animal the same.
      Animal sparrow = new Bird();
      animalList.add(sparrow);

      Animal collie = new Mammal();
      animalList.add(collie);

      Animal minno = new Fish();
      animalList.add(minno);

      System.out.println("Moving animals:");
      // we iterate over the whole list
      for (Iterator aIterator = animalList.iterator(); aIterator.hasNext();) {
         Animal aAnimal = (Animal) aIterator.next();
         aAnimal.move();
      }

      System.out.println("Getting animals to speak:");
      for (Iterator aIterator = animalList.iterator(); aIterator.hasNext();) {
         Animal aAnimal = (Animal) aIterator.next();
         aAnimal.speak();
      }
   }
}

Expanding the hierarchy with Bees and Butterflies

This next section is not so much about the abstract class that we have been talking about, but about some ways that classes interact when extending each other. I had these created, so if you have time, lets look at Bee and Butterfly. This is the new class diagram:

animals2 

Both of these classes extend the class Bird. Bees and Butterflies may not really be birds, but they do fly, so just bear with me here. First Bee.
 

The Bee

The Bee class extends Bird. This means that we get all the functionality of the Bird class for free. But some of it is not correct. In order to modify the Bee class to get what we want, we will override the methods. We will create a method with the exact same signature as the Bird class has. That means that when the Bee is used, even if cast to a Bird, or Animal, it will use the new overridden methods.

public class Bee extends Bird {

   /**
    * This constructor is calling the constructor of the parent class (Bird) Do
    * we need it? Nope. If we didn't have it, java would have created a
    * "default" constructor that would call super() on Bird, exactly like we
    * have here. Run CreateAnimals4 with and without this constructor.
    */
    public Bee() {
      super();
    }

   /**
    * Implements the getType() method from the Animal interface
    */
   public String getType() {
      return "bee";
   }

   /**
    * Implements the speak() method from the Animal interface
    */
   public void speak() {
      // what would this do uncommented?
      super.speak();
      System.out.println("Buzzzz Buzzzzzzzz");
   }

   /**
    * This method has nothing to do with an interface or a base class. This
    * method is just in the Bee class. For this example, an Animal, or a Bird
    * doesn't know how to gather honey. You can only execute this method on a
    * Bee.
    */
   public void gatherHoney() {
      System.out.println("Gathering honey");
   }
}

 

Notice that the getType() method has been overridden to return “bee”. Also look at the speak() method. What does it do? The super.speak() method is calling the same method on the parent class. This is usually a good idea to do, so that the parent gets a chance to update it’s self too. We didn’t do that for getType(), but we knew exactly what getType() did, and knew that we didn’t want that functionality.

Notice that there is a gatherHoney() method here too. Since all animals and birds don’t gather honey, we decided to put it only on the Bee class. See AnimalTest4.java. Don’t forget to watch the constructor and method flows in the debugger in Eclipse if you can.

public class AnimalsTest4 {
   public static void main(String[] args) {

      Bird aBird = new Bird("Tweety");
      Bee aJohneyBee = new Bee();
      Animal aJaneBee = new Bee();

      System.out.println("Moving animals:");
      aBird.move();
      aJohneyBee.move();
      aJaneBee.move();

      System.out.println("Getting animals to speak:");
      aBird.speak();
      aJohneyBee.speak();
      aJaneBee.speak();

      aJohneyBee.gatherHoney();
       // why does uncommenting these line give an error?
      // aBird.gatherHoney();
      // aJaneBee.gatherHoney();
   }
}

 

The Butterfly

This class also extends Bird, therefore we get some behavior for free. For this example though, we are dealing with the Borg Butterflies. This example is to hammer home how static variables work, and shows just how complicated the method calling within a hierarchy can be.

public class Butterfly extends Bird {

   /**
    * These variables are not created for every Butterfly object. They are just
    * created once. It allows us to keep track of things that all butterflies
    * need to know.
    */
   private static int NUMBER_OF_BUTTERFLIES = 0;
   private static int NUMBER_FLYING = 0;

   private int butterflyId = 0;

   /**
    * This constructor calls it's parents constructor first. Which class? The
    * Bird. Look back at the Bird's constructor. It calls it's parents
    * constructor (BaseAnimal) which deals with the name, then adds a print
    * statement. Then we return to this constructor here to set up this class.
    * 
    * We set up each instantiation of this class with a unique butterflyId that
    * increases everytime a new butterfly is created.
    */
   public Butterfly() {
      super();
      NUMBER_OF_BUTTERFLIES++;
      butterflyId = NUMBER_OF_BUTTERFLIES;
   }

   /**
    * Notice the green triangle beside this method in eclipse. This means that
    * this method is overriding the getType method in it's parent. This method
    * will be used instead of the one from the parent. (Bird)
    */
   public String getType() {
      return "butterfly";

      // what if we wanted to use the getType method from Bird?
      // what would this do?
      // return "("+super.getType()+") butterfly";
   }

   /**
    * This is fun. This method also overrides a method in the parent, the Bird.
    * The fun part is how it can be used. Go back and look at the
    * getDescription() method on the BaseAnimal class. It calls getName().
    * So... if the move() method on our Butterfly is called, it calls the
    * move() method on Bird, which prints out a statement that calls
    * getDescription() from the BaseAnimal class that get's the name by calling
    * the getName method that is overriden by the Butterfly class. Get it? Just
    * look at the output from AnimalsTest5.
    */
   public String getName() {
      StringBuilder aBuilder = new StringBuilder();
      aBuilder.append(String.valueOf(butterflyId));
      aBuilder.append(" of ");
      aBuilder.append(String.valueOf(getNumberOfButterflies()));
      return aBuilder.toString();
   }

   /**
    * This is a member method of Butterfly. That means you can ask any
    * Butterfly object for the number of butterflies, and it will know.
    */
   public int getNumberOfButterflies() {
      return NUMBER_OF_BUTTERFLIES;
   }

   /**
    * This is a static way to access the number of butterflies not flying. To
    * access this, you do not use a Butterfly object, but the class that wants
    * to use this would do a
    * Butterfly.getNumberOfButterfliesNotFlyingStaticly();
    * 
    * Like the static variables themselves, this static method doesn't belong
    * to an object, but to the class as a whole.
    */
   public static int getNumberOfButterfliesNotFlyingStaticly() {
      return Butterfly.NUMBER_OF_BUTTERFLIES - Butterfly.NUMBER_FLYING;
   }

   /**
    * Why doesn't this work?
    */
   // public static int getButterflyIdStaticly() {
   // return butterflyId;
   // }
	
   /**
    * This is a member method of Butterfly. That means you can ask any
    * Butterfly object for the number of flying butterflies, and it will know.
    */
   public int getNumberFlying() {
      return NUMBER_FLYING;
   }

   /**
    * This move method overrides the move method from Bird. Yes, I realize that
    * if you were to call the move method continually on the same butterfly you
    * would have an incorrect count. What can I say, it's a contrived example.
    */
   public void move() {
      super.move();
      // the same as NUMBER_FLYING = NUMBER_FLYING + 1
      NUMBER_FLYING++;
   }
}

For example. Look at the move() method. When it is called, we call the parent’s move() method. So we look at Bird.move(). It prints out that some description is flying. The description comes from the base class. BaseAnimal.getDescription() builds it’s description by calling getType(). Where is this implemented? Well, it is implemented on Bird, but Butterfly overrides it, so “butterfly” gets returned. Then getName() is called. That is a method in the BaseAnimal, but Butterfly overrides it, because Borg don’t have names. They are x of y. Get it? This is a great one to follow in the debugger.

One good take a way from this is that when ever a class extends and overrides a method, that method is always executed for the instance of the extending class. To clarify; If you create a Butterfly and cast it to a Bird, and call getType(), you will still get “butterfly” back. It will still execute Butterflies getType() method.

If you have a Butterfly and cast it to an Animal, and call move on it, it will still execute the move() method from the Butterfly. You will not be able to call gatherHoney() on your Butterfly that has a type of Animal, but it is still a Butterfly.

Look at AnimalsTest5.java and AnimalsListTest2.java.

public class AnimalsTest5 {
   public static void main(String[] args) {

      Butterfly aButterfly1 = new Butterfly();
      Butterfly aButterfly2 = new Butterfly();
      Butterfly aMonarch = new Butterfly();

      // How many butterflies did we create?
      System.out.println("(A)Number of butterflies:"
            + aButterfly1.getNumberOfButterflies());

      Animal aSmallAnimal = new Butterfly();
      Animal aButterfly5 = new Butterfly();

      // Now how many butterflies did we create?
      System.out.println("(B)Number of butterflies:"
            + aButterfly1.getNumberOfButterflies());

      // Does it matter which butterfly we ask?
      System.out.println("(C)Number of butterflies:"
            + aMonarch.getNumberOfButterflies());

      // why can't we ask aButterfly5?
      // System.out.println("(D)Number of
      // butterflies:"+aButterfly5.getNumberOfButterflies());

      // or can we?
      // System.out.println("(E)Number of butterflies:"
      // + ((Butterfly) aButterfly5).getNumberOfButterflies());

      // how many are flying?
      System.out.println("(F)Number flying:" + aButterfly1.getNumberFlying());

      // lets ask a few to speak
      aButterfly2.speak();
      aSmallAnimal.speak();

      // lets get a few to fly
      aButterfly1.move();
      aButterfly5.move();

      // how many are flying now?
      System.out.println("(G)Number flying:" + aButterfly1.getNumberFlying());
   }
}
public class AnimalsListTest2 {
   public static void main(String[] pArgs) {

      // using Java 5 typed list. Only objects of type
      // Animal can go in this list.
      List animalList = new ArrayList();

      // if we don't need references to the animals
      // we create, don't create local variables for them.
      animalList.add(new Bird());
      animalList.add(new Mammal());
      animalList.add(new Fish());
      animalList.add(new Bee());
      animalList.add(new Butterfly());

      System.out.println("Moving animals:");
      // We will use a Java 5 "for each" loop to minimize typing.

      // if we did it with a typical for loop, it would look like this
      // for (int i = 0; i < animalList.size(); i++) {
      // Animal aAnimal = (Animal) animalList.get(i);
      // aAnimal.move();
      // }

      // using Java 5 typed lists we don't need casting.

      // for each means: for each and every object in a list
      // ie. for every Animal in animalList, assign it to aAnimal

      // here is the Java5 for each loop
      for (Animal aAnimal : animalList) {
         // we don't need to know what type of animal
         // we are moving, just that we want them to move.
         aAnimal.move();
      }

      System.out.println("Getting animals to speak:");
      // we don't need to know what type of animal
      // is speaking, just that we want them to speak.
      // for every Animal in animalList, assign it to aAnimal
      for (Animal aAnimal : animalList) {
         aAnimal.speak();
      }
   }
}

[drain file 11 show tableRow]

[drain file 12 show tableRow]

Type Download Name Version Size Updated Hits
Series NavigationIntro to Java InterfacesIntro to Java Reflection