Java 11 Developer Certification - Inheritance - Abstract Class

January 16, 2021

What we are covering in this lesson

  1. Introduction
  2. Code Examples
  3. Abstract class rules
  4. final methods in an abstract class

Introduction

An abstract class is a class that is declared with the abstract modifier and it may or may not include abstract methods. An abstract class, defers some or all of its implementation to its subclasses, and so the abstract classes themselves cannot be instantiated. An abstract class is used to define common attributes and behaviour, for a set of classes that will extend it. It’s usually modelled after an abstract concept, a type you would be unlikely to create an instance of, but which could be used to describe common features of a set of objects.

In the previous section, we looked at the Animal and Dog class. The Animal class is a perfect example of an abstract class because creating an individual object of Animal is unlikely to happen. And yet you can identify many common attributes across several types of animals into the Animal Class, as well as common behaviour that any type of animal might have (walk, bite, sit, play for example). A class that is not abstract is called a concrete class.

The difference between using an abstract class and a concrete class is that the abstract class usually requires a subclass to implement its methods, thereby forcing commonality of interface. It’s similar to an interface in this way, but it also permits providing functionality that subclasses might have in common, and that you would just implement once on the abstract class.

The absolute minimum declaration of an abstract class is shown below. abstract class TestAbstractClass {}

Any method that is not implemented and does not have a method body, must be declared abstract. Any class which has atleast one abstract method, must be declared abstract. That is because we would not want to create an object that does not have its behaviour implemented yet.

Code Examples

Lets looks at a simple piece of code related to abstract class.

We will create an abstract class Vehicle, and this class will support a set of classes that can be termed vehicle at the most general level and which have a common set of attributes (variables) and common set of behaviours (methods).

Auto class is subclass of the Vehicle abstract class, and as we know if we extend an abstract class, we must implement all the methods present in the abstract class, unless otherwise we declare the subclass as abstract. We have implemented all the methods in Auto class. And we also have a main class, the output is as displayed below.

Vehicle.java

package maxCode.online.inheritance;

public abstract class Vehicle {
	// We create an enum which describes possible subclasses
	protected enum VehicleType {
		Automobile, Motorcycle, Moped, Bicycle, Scooter
	}

	// We define some attributes all vehicles would have in common
	private VehicleType type;
	private String owner;
	private String make;

	// Constructor will be the method we use to set data
	public Vehicle(VehicleType type, String owner, String make) {
		this.type = type;
		this.owner = owner;
		this.make = make;
	}

	// We use IntelliJ's generated toString method
	public String toString() {
		return "Vehicle{" + "type=" + type + ", owner='" + owner + '\'' + ", make='" + make + '\'' + '}';
	}

	// We create methods we want concrete subclasses to be forced to implement
	public abstract void drive();
	public abstract void park();
	public abstract void makeNoise();
}

Auto.java

package maxCode.online.inheritance;

public class Auto extends Vehicle {
	// Create a constructor that is a pass thru to Vehicle constructor
	public Auto(VehicleType type, String owner, String make) {
		super(type, owner, make);
	}

	// Implement Vehicle's drive method for an Auto
	public void drive() {
		System.out.println("Release Brake, go");
	}

	// Implement Vehicle's park method for an Auto
	public void park() {
		System.out.println("Parallel or back in?");
	}

	// Implement Vehicle's makeNoise method for an Auto
	public void makeNoise() {
		System.out.println("Vroom Vroom");
	}

	// Main method creates an instance of Auto and demonstrates calling the
	// concrete methods
	public static void main(String[] args) {
		Auto car = new Auto(VehicleType.Automobile, "Allen", "Ford");
		System.out.println(car);
		car.drive();
		car.park();
		car.makeNoise();
	}
}

Output

Vehicle{type=Automobile, owner='Allen', make='Ford'}
Release Brake, go
Parallel or back in?
Vroom Vroom

Another example below shows how we can make the subclass abstract if we dont want to implement all the methods from the parent class.

AutomatedVehicle is an abstract class that extends another abstract class Vehicle, and supports special sub types of Vehicles those that are automated. In this class, we call the autoDrive and autoPark methods from the original drive and park methods. This class has only implemented two of the three abstract method from the parent class Vehicle. Any concrete class which would extend AutomatedVehicle class, will need to implement the autoDrive and autoPark methods. Just to show that, we have the AutomatedAuto class, quite similar to the Auto class, except that it extends the AutomatedVehicle class. We just need to implement the autoDrive and autoPark method here. Although in the main method, we call the drive and park methods, which will in turn call the autoDrive and autoPark method implementations respectively. So you can see that abstract methods and inheritance support extensibility of the application.

AutomatedVehicle.java

package maxCode.online.inheritance;

public abstract class AutomatedVehicle extends Vehicle {
	// Constructor is pass thru to Vehicle constructor
	public AutomatedVehicle(VehicleType type, String owner, String make) {
		super(type, owner, make);
	}

	// This class implements park(), but calls an abstract method defined
	// on this class. Any class that extends the AutomatedVehicle no
	// longer has to implement park(), but has to implement autoPark.
	public void park() {
		autoPark();
	}

	// This class implements drive(), but calls an abstract method defined
	// on this drive
	public void drive() {
		autoDrive();
	}

	// Any concrete class that extends the AutomatedVehicle has to
	// implement autoPark
	abstract void autoPark();

	// Any concrete class that extends the AutomatedVehicle has to
	// implement autoDrive
	abstract void autoDrive();
}

AutomatedAuto.java

package maxCode.online.inheritance;

public class AutomatedAuto extends AutomatedVehicle {
	// This constructor is a pass thru method to the constructor on AutomatedVehicle
	public AutomatedAuto(VehicleType type, String owner, String make) {
		super(type, owner, make);
	}

	// Implements autoDrive(), required by AutomatedVehicle extension
	public void autoDrive() {
		System.out.println("Driving it myself");
	}

	// Implements autoDrive(), required by AutomatedVehicle extension
	public void autoPark() {
		System.out.println("Parking it myself");
	}

	// Implements autoDrive(), required by Vehicle extension, because
	// Automated Vehicle punted on the implementation - actually it
	// made no assumptions about how the subclass would implement it.
	public void makeNoise() {
		System.out.println("Beep, Beep");
	}

	// Main method instances an object of type AutomatedAuto.
	public static void main(String[] args) {
		AutomatedAuto automatedCar = new AutomatedAuto(VehicleType.Automobile, "Martha", "Tesla");
		System.out.println(automatedCar);

		// drive() and park() are not even methods on AutomatedAuto
		// but we can use them because of inheritance and best part of all,
		// they execute autoDrive() and autoPark() for this type of object.
		automatedCar.drive();
		automatedCar.park();
		automatedCar.makeNoise();
	}
}

Output

Vehicle{type=Automobile, owner='Martha', make='Tesla'}
Driving it myself
Parking it myself
Beep, Beep

Abstract class rules

We have already gone through these rules, but lets summarize them below.

  • Any class that has an abstract method must be declared abstract.
  • On the other hand, an abstract class does not necessarily need to have any abstract methods.
  • Any subclass must implement ALL of the abstract class’s abstract methods, or be declared abstract itself if it does not.
  • An abstract class can have implemented methods of any type.
  • An abstract class can have attributes and other inner types such as class, enum, and interface.
  • An abstract class cannot also be declared as final. The two modifiers are mutually exclusive.
  • An abstract class cannot be declared as private because no other class would be able to extend it.
  • An abstract method cannot be declared as private because no other class would be able to extend it.
  • An abstract method cannot be static. A static method cannot be overridden so a static abstract method makes no sense.
  • We cannot create an abstract constructor.

final methods in an abstract class

As we discussed previously, an abstract class cannot be declared final. But we can declare a method final within the abstract class, but as you would have already understood, such method should also have its implementation.

The class AbstractClass has a constant variable, a final static method, a final method and two abstract methods. The ConcreteClass extends the AbstractClass, and implements the abstract methods. Now from the main class, we call the static final method of AbstractClass, and the other methods as well.

package maxCode.online.inheritance;

abstract class AbstractExample {
	// A constant can be defined on an abstract class
	public static final String ABSTRACT_CONSTANT = "Abstract";

	// a final static method can be declared on an abstract class
	public static final void doThisAndOnlyThis() {
		System.out.println("static final method is ok abstract class");
		System.out.println("My constant is " + ABSTRACT_CONSTANT);
	}

	// This is a final method, but is not static. This means any
	// subclass can call it, but they cannot override it.
	public final void doThisAndThat() {
		System.out.println("My method may be final, " + "but it can still support polymorphism");
		System.out.println(doThis() + " and " + doThat());
	}

	// Implementing classes need to implement abstract methods.
	abstract String doThis();
	abstract String doThat();
}

// ConcreteExample implements the two abstract methods it inherits.
class ConcreteExample extends AbstractExample {
	String doThis() {
		return "In do this";
	}

	String doThat() {
		return "In do that";
	}
}

// Now we test our final methods...
public class AbstractClassExample {
	public static void main(String[] args) {
		// Call to the final static method...
		ConcreteExample.doThisAndOnlyThis();

		ConcreteExample c = new ConcreteExample();
		// Call to the final method defined on the abstract parent.
		c.doThisAndThat();
	}
}

Output

static final method is ok abstract class
My constant is Abstract
My method may be final, but it can still support polymorphism
In do this and In do that

The compiler will throw an error if we create a private constructor in the abstract class, and some other concrete class extends this abstract class.

Thats all for Abstract Classes, see you in the next section, where we will discuss Polymorphism!