DEV Community

Andrew Capp
Andrew Capp

Posted on

Java: Why Have Abstract Classes?

I’ve been learning Java recently and a new concept for me is the idea of abstract classes. While the concept is already kind of “abstract” (pun intended), what I don’t understand is why you would use an abstract class when you can just use a regular class or an interface.

Abstraction in OOP (Object Oriented Programming)

To review, the concept of abstraction in OOP is hiding the implementation details so the developer can focus on the functionality (how the item is used). For example, a TV remote is an abstraction in that you don’t have to know how the remote works in order to use it – you just need to know which button to push.

What is an Abstract Class?

In Java, there are two ways to create an abstract class:

Use abstract keyword: abstract class Car { // Code… }

or

Use interface keyword: interface Animal { // Code… }

We are focusing on the abstract class here. However, one of the differences between an abstract class and an interface is that 0-100% of the methods in an abstract class can be abstract while 100% of the methods in an interface are abstract (with some exceptions).

An abstract class is similar to a regular class in Java with the following differences:

  • Must be declared using the abstract keyword.
  • It can have abstract and non-abstract methods.
  • It cannot be instantiated (you cannot create objects directly from an abstract class).
  • You must extend an abstract class (inherit from it) in order to use it.

What is an Abstract Method?

An abstract method must also be declared using the abstract keyword:
abstract void drive();

It cannot have any code in the method – just a method signature like in the above example.

Any regular class inheriting from an abstract class must implement every abstract method. This means you are required to provide your own method to replace an abstract one.

What are Abstract Classes Used For?

So back to my original question – why do we use them? It turns out that abstract classes are very useful in Java. They are used when you have common functionality and need to implement some custom behavior as well. For example, let’s say that you need to download data from different internet sites. You can use an abstract parent class to handle the communication and downloading of the data while the individual child classes implement their own way to handle the data that is downloaded.

Going back to the OOP concept of abstraction - this makes sense. We are letting the abstract parent class handle the details of communicating over the internet while we focus on handling the specific data being downloaded – which is likely to be different for each site we visit.

An example of this might look like:

abstract class internetLink {
// methods to handle downloading data from the internet

abstract void handleData(); // method to handle the data to be implemented by our classes
}

class newsDownload extends internetLink {
void handleData() {
// code to handle the downloaded news data
}
}

class friendsDownload extends internetLink {
void handleData() {
// code to handle the downloaded friends data
}
}

class chatDownload extends internetLink {
void handleData() {
// code to handle the downloaded chat data
}
}

So, each of our download classes handles different data. We can write our internetLink class to handle the actual downloading while our download classes implement methods that handle the data. In this case we only need to write a method to do something with the data. We don’t have to worry about the actual downloading – the internetLink class handles that for us!

This is called the Template Method Design Pattern and is a popular design pattern in object oriented programming languages.

This concept makes more sense to me now and I hope this makes more sense to you as well!

Latest comments (1)

Collapse
 
siy profile image
Sergiy Yevtushenko

Since Java 8 many traditional use cases for abstract classes can be handled by interfaces with default methods. Often it's possible to express class functionality via single method. In such cases particular implementation of the interface can be even represented as lambda:

public interface Mapper<T1> {
    Promise<Tuple1<T1>> id();

    default <R> Promise<R> map(final FN1<R, T1> mapper) {
        return id().map(tuple -> tuple.map(mapper));
    }

    default <R> Promise<R> flatMap(final FN1<Promise<R>, T1> mapper) {
        return id().flatMap(tuple -> tuple.map(mapper));
    }
}
Enter fullscreen mode Exit fullscreen mode

Now when one need to create implementation, it will look so:

    static <T1> Mapper1<T1> doWithAll(final Promise<T1> promise1) {
        return () -> all(promise1);
    }
Enter fullscreen mode Exit fullscreen mode