AbstractionS2C Home « Abstraction

In this lesson we continue our investigation of OO concepts by looking at abstraction. In the previous five lessons we looked at inheritance and how it enables us to avoid code duplication by setting a protocol for a group of classes. We used the extends keyword to subclass this common behaviour into more specific classes, whilst still being able to use the more abstract mechanics of the superclass. We set up an inheritance hierarchy to illustrate the mechanics of inheritance and how method invocation works. Something isn't right with our hierarchy though, so let's take a look at this with the following slideshow:

abstract 1

When we last visited Vehicle Town, we had subclassed the Truck class, so our trucks could do more specific tasks. But in Vehicle town people are scared, there have been reports of strange vehicles and trucks roaming the streets. The mayor wants us to look into this and see whats going on.

abstract 2

We can create instances of Vehicle and Truck which makes no sense. What would these objects look like? They're just abstractions.



abstract 3

We can use the abstract keyword on the class so these objects cannot be instantiated but can still be extended.



Abstract Classes Top

In our class inheritance tree for Vehicle Town we need to stop instantiation of the Vehicle and Truck class, as these are abstractions for their subclasses. To do this we use the abstract keyword in the class definition. This means that only static members of this class can ever be used unless we create a non-abstract subclass. So abstract classes need to be extended if we want to use any non-static members within the class. This also means that you can't declare a class as both abstract and final or the compiler complains. Non-abstract classes are known as concrete classes. Lets make our Vehicle class abstract.


package com.server2client;
/*
  A Vehicle Class
*/ 
public abstract class Vehicle { // Add the abstract keyword to the class definition

    ...

}

We already have a TestVehicle class we coded earlier so lets rerun this.

run vehicle test class
Screenshot 1. Running the TestVehicle class.

The above screenshot shows the output of testing our abstracted Vehicle class. The compiler won't allow us to instantiate the Vehicle class which is exactly what we want.

Time to make our Truck class abstract as well.


package com.server2client;
/*
  A Truck Class
*/ 
public abstract class Truck { // Add the abstract keyword to the class definition

    ...

}

We already have a TestTruck class we coded earlier so lets rerun this.

run truck test class
Screenshot 2. Running the TestTruck class.

The above screenshot shows the output of testing our abstracted Truck class. The compiler won't allow us to instantiate the Truck class which is exactly what we want.

Abstract Methods Top

We can also mark methods as abstract and if we do so the class must also be marked as abstract, as we can't have any abstraction in a concrete class. Whereas an abstract class must be extended in a subclass, an abstract method must be implemented in the first concrete subclass. This provides a contract that the compiler makes sure is met in the first concrete subclass. But why is this useful to us? Well it ensures that the first concrete subclass keeps to the contract by implementing any abstract methods of the superclass. To get to grips with this we need an example. We will code a new Truck subclass called Tanker.


package com.server2client;
/*
  A Tanker Class
*/ 
public class Tanker extends Truck {

}

Save and compile our Tanker subclass in directory   c:\_OOConcepts in the usual way.

Lets test the new Tanker subclass to make sure it works:


package com.server2client;
/*
  Test Class for Tanker
*/ 
public class TestTanker {

    public static void main (String[] args) {
        Tanker tanker = new Tanker();
        tanker.setChassis("8-axle chassis");
        tanker.setMotor("16 stroke");
        tanker.setWheels(10);
        System.out.println("Our Tanker has a " + tanker.getChassis() + ", " + tanker.getMotor() 
                                            + " motor and has " + tanker.getWheels() + " wheels.");
        tanker.service(3); 
        tanker.carry(2); 
        tanker.load("10,00 gallons of diesel"); 
    }
}

run Tanker class
Screenshot 3. Running the TestTanker class.

The above screenshot shows the output of testing our Tanker subclass. If you look closely at the output we have a problem with the load() method, it is using the load() method from the Vehicle class. We don't really want this, we want our Tanker class to override the load() method like all subclasses of the Truck class do. So how to do this? Well we use method abstraction to ensure the first concrete subclass of our Truck class implements the abstracted method. To see this in action we will add an abstract load() method to our Truck abstract class:


package com.server2client;
/*
  A Truck Class
*/ 
public abstract class Truck extends Vehicle {
    /*
      Our carry() override method
    */ 
    public void carry(int tonnage) {
        System.out.println("Our truck can carry up to " + tonnage + " tons of cargo."); 
    }
    /*
      Our abstract load() method
    */ 
    public abstract void load(String payload); 
}

Well the code for our abstract load() method certainly looks different! Abstract methods have no body and are terminated by a semicolon.

We should rerun a subclass test to make sure everything is still hunky dory. So recompile Classes HGV and TestHGV and run the latter just to make sure everything still works:.

run HGV class
Screenshot 4. Rerunning the TestHGV class.

The above screenshot shows the output of testing our HGV subclass. The class still works as intended. For our purposes we have already overridden the load() method in the HGV subclass and thats what implementing an abstract method is, it's the same as overriding a method from a concrete superclass. This also means the same rules apply as those for overriding method rules

Lets see what happens if we don't implement an abstract method, by trying to recompile our new Tanker class:


package com.server2client;
/*
  A Tanker Class
*/ 
public class Tanker extends Truck {

}

run no imp Tanker class
Screenshot 5. Running the TestTanker class.

As you can see from the screenshot we get a compiler error because we didn't implement the abstract load() method from the Truck superclass. Now we are assured that any concrete subclass of the Truck class implements the abstract load() method.

Related Quiz

OO Concepts Quiz 8 - Abstraction

Lesson 8 Complete

In this lesson we continued our investigation of OO concepts by looking at abstraction.

What's Next?

We continue our investigation of OO concepts by learning about polymorphism.