Chapter 5 - Java for Beginners Course

Abstract Classes and Methods

Java allows us to define classes that cannot be instantiated and that may have methods defined without any implementation. These are called abstract classes and are used as parent classes in hierarchies of related classes that have shared state/behavior, but where specific implementation details are added in the subclasses.

The abstract keyword allows us to define both abstract classes and methods, for example:

public abstract class AbstractClassName {
    //...
    abstract void exampleAbstractMethod(int parameter);
    //...
}

Details about abstract classes and methods

Abstract classes can’t be instantiated, this is, we can’t do new AbstractClassName() for example. The opposite of an abstract class is called a concrete class which are the type of classes we’ve seen in all our previous sections.

Abstract methods don’t have a body and can only be defined inside an abstract class. These methods just define a signature of a method that should be implemented in concrete subclasses.

Abstract methods are a type of contract between a superclass and its subclasses where the superclass defines methods that should exist in the subclasses but doesn’t know the implementation details. An abstract class doesn’t need to define abstract methods.

In every other respect, an abstract class behaves in the same way as a concrete class.

An example using Numeral Systems

To clarify the concept, let’s take a look at an example. In this scenario let’s assume we are building an application that allows us to convert and print out a number into different numeral systems. For example, if we provide number 4 to a binary number system, we should get "100" as the output, or if we provide 10 to a Roman number system we should get "X".

Based on our requirement above, our numeral system should have 2 behaviors, one to print and another to convert a number, so we could think of defining a class in the following lines:

public class NumeralSystem {
    public String convert(int number) {
        String result = "";
        // logic to convert the number goes here...
        return result;
    }

    public void print(int number) {
        String convertedNumber = convert(number);
        System.out.println(convertedNumber);
    }
}

Now, the print method on the class above is pretty well defined and we can say it works for most of our numeral systems, as in, it converts the number using the convert method and then prints out the result. However, you can see that the convert method is still undefined, we don’t know what it needs to do as it really depends on the number system we’re implementing.

As some of the state or behavior above can be shared across multiple similar classes, and it doesn’t make sense for users to create objects of NumeralSystem, we could think about converting our existent class into abstract. Also, as the convert method really depends on the numeral system, we should also convert it into an abstract method to force concrete classes to implement it:

public abstract class NumeralSystem {

    public abstract String convert(int number);

    public void print(int number) {
        String convertedNumber = convert(number);
        System.out.println(convertedNumber);
    }

}

Now, let’s define an initial subclass of NumeralSystem to represent a binary numeral system:

public class BinaryNumeralSystem extends NumeralSystem {

}

In its current form, the Java compiler will throw an error stating that our BinaryNumeralSystem must implement the abstract convert method inherited from NumeralSystem. This is because our BinaryNumeralSystem is a concrete class (it doesn’t have the abstract keyword) and concrete classes cannot have abstract methods, so we need to provide an implementation.

So, let’s do just that:

public class BinaryNumeralSystem extends NumeralSystem {
    @Override
    public String convert(int number) {
        return Integer.toBinaryString(number);
    }
}
Integer.toBinaryString is one of multiple utility methods provided in Java to do common operations on numbers. This is an static method, a topic we’ll cover in this chapter.

Once we provide the implementation of the convert method, the compilation error is gone.

Note that the Java compiler didn’t generate an error with regards to the print method at any point as it is a concrete method (it has an implementation) so our concrete class doesn’t need to do anything with it. We can of course override it if required.

For the purpose of the example, let’s define another numeral system:

public class HexadecimalNumeralSystem extends NumeralSystem {
    @Override
    public String convert(int number) {
        return Integer.toHexString(number);
    }
}

We’ve now implemented two separate NumeralSystem subclasses that share a common behavior (print) and have a different way of handling the convert method.

Let’s give this a try:

In the example code run the JavaAbstractClassApp
BinaryNumeralSystem binary = new BinaryNumeralSystem();
HexadecimalNumeralSystem hexa = new HexadecimalNumeralSystem();

int number = 15;

System.out.println(number + " in binary is represented as: ");
binary.print(number);

System.out.println(number + " in hexadecimal is represented as: ");
hexa.print(number);

Output:

15 in binary is represented as:
1111
15 in hexadecimal is represented as:
f
Code in GitHub

Get the code for this tutorial using the links below.

Project Repo
Download code for this step
Main class for this step
Dependencies

This is a list of recommended tutorials or courses that might be useful before starting this one.

Contents
Welcome to the Course!
Course Introduction
Chapter 1 - Building Blocks
Quick introduction to Java Variables Classes And Objects Class Example - Defining a class Object Examples - Creating instances Java Application Example - Running our first app Accessing class members - The dot operator Packages - Organizing the code
Chapter 2 - Primitives and Operators
Primitives Arithmetic Operators Assignment Operator Unary Operators Equality and Relational Operators Conditional Operators
Chapter 3 - Statements and Control Flow
Expressions Statements If-Then Statement If-Then-Else Statement More If Statements Switch Statement While and Do-While Statements For Statement Branching Statements Exception Handling
Chapter 4 - Code Example
Example Project - A Simple Vending Machine Adding money Delivering Items Giving Change
Chapter 5 - Classes and Interfaces
Introduction Access Level Modifiers Class Declaration - Class, Methods and Fields Class Declaration - Constructors Inheritance Basics Inheritance - Constructors Inheritance - Methods and Fields Polymorphism Abstract Classes and Methods Interfaces Static Class Members Class Composition Final Classes and Class Members Generic Classes
Chapter 6 - Base Object Behaviors
Introduction Type Comparison Type Casting Object Equality - The Contract Object Equality - Common Pitfalls Object String Representation Garbage Collection Object Comparison Primitive Wrappers and Autoboxing
Chapter 7 - Data Structures
Introduction Arrays - Declaration and Creation Arrays - Basic Operations Core Collection Interfaces List and ArrayList - Basic Operations ArrayList Internals Introduction to Hash Tables Map and HashMap - Basic Operations Set and HashSet - Basic Operations
Chapter 8 - Anonymous classes and lambdas
Introduction Filtering a List Anonymous Classes Lambdas Built-in Functional Interfaces
Chapter 9 - Streams
Introduction Creating Streams Intermediate Operations Terminal Operations