Abstract Classes vs Interfaces in Java: When and How to Use Them

@Harsh
4 min readOct 10, 2024

--

As I continue deepening my understanding of Java, I recently revisited two foundational concepts: abstract classes and interfaces. These concepts play a critical role in designing flexible, reusable, and maintainable code in object-oriented programming. Along the way, I also explored how multiple inheritance, although not directly supported in Java, can be achieved using interfaces.

In this blog, I will cover:

  1. What is an Abstract Class?
  2. What is an Interface, and How Does It Differ from an Abstract Class?
  3. Why Java Doesn’t Support Multiple Inheritance and How Interfaces Provide a Solution?

1. What is an Abstract Class?

An abstract class in Java is a class that cannot be instantiated on its own. Instead, it serves as a blueprint for other classes that extend it. Abstract classes can contain both abstract methods (methods without implementation) and concrete methods (methods with implementation). It is typically used when you want to define common functionality for a group of subclasses, but also leave some methods for those subclasses to implement.

Key Features of Abstract Classes:

  • Cannot be instantiated: You cannot create an object of an abstract class directly.
  • Abstract methods: It can have abstract methods, which must be implemented by any subclass.
  • Concrete methods: It can also contain fully implemented methods, giving subclasses some default behavior.
  • Single inheritance: Like any other class, an abstract class can be extended by only one subclass at a time.
abstract class Animal {
abstract void sound(); // Abstract method

public void eat() { // Concrete method
System.out.println("This animal is eating.");
}
}

class Dog extends Animal {
@Override
void sound() {
System.out.println("The dog barks.");
}
}

In this example, Animal is an abstract class with one abstract method (sound()) and one concrete method (eat()). Any subclass, like Dog, must implement the sound() method.

2. What is an Interface?

An interface in Java is like a contract for classes. It is a completely abstract class, meaning all of its methods are abstract (until Java 8, when default and static methods were introduced). Interfaces are used to specify behaviors that a class must implement, but they don’t provide any concrete behavior themselves.

Key Features of Interfaces:

  • All methods are abstract by default.
  • A class can implement multiple interfaces, which provides a workaround for Java’s lack of multiple inheritance.
  • Interfaces do not have instance variables, though they can have static final constants.
interface Flyable {
void fly(); // Abstract method
}

interface Swimmable {
void swim(); // Abstract method
}

class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("The duck flies.");
}

@Override
public void swim() {
System.out.println("The duck swims.");
}
}

In this example, Duck implements two interfaces, Flyable and Swimmable. This demonstrates one of the key advantages of interfaces: multiple inheritance (which I’ll discuss in more detail below).

Differences Between Abstract Classes and Interfaces

While both abstract classes and interfaces help in defining the structure of a class, they have distinct differences:

3. Multiple Inheritance in Java and How It Is Achieved via Interfaces

Java doesn’t support multiple inheritance (a class cannot extend more than one class). This restriction exists to avoid the “diamond problem”, which arises when a class inherits from two classes that both provide different implementations of the same method. This could create ambiguity about which method to use.

However, Java provides a solution to this problem through interfaces. Since a class can implement multiple interfaces, Java developers can achieve the functionality of multiple inheritance without its complications.

Why Multiple Inheritance Is Not Allowed in Java:

  • Ambiguity: Multiple inheritance can lead to ambiguity when two or more parent classes have methods with the same signature.
  • Complexity: The code becomes harder to maintain and debug.
  • Diamond Problem: In languages that support multiple inheritance (like C++), the “diamond problem” can occur, leading to confusion over which inherited method to execute.

Achieving Multiple Inheritance with Interfaces:

Since interfaces provide no method implementations (before Java 8), there’s no risk of conflicting method implementations, allowing a class to implement as many interfaces as it needs.

interface Printable {
void print();
}

interface Showable {
void show();
}

class Document implements Printable, Showable {
@Override
public void print() {
System.out.println("Printing document.");
}

@Override
public void show() {
System.out.println("Showing document.");
}
}

Here, Document implements both Printable and Showable interfaces, thereby achieving multiple inheritance behavior without ambiguity.

4. Revisiting Abstraction and Interfaces

Both abstract classes and interfaces are ways to achieve abstraction in Java, but they serve slightly different purposes.

  • Abstract classes allow you to define some default behavior while forcing subclasses to implement specific methods.
  • Interfaces focus purely on behavior, providing no concrete methods.

When to Use Abstract Classes vs. Interfaces:

  • Use an abstract class when you want to share code among several closely related classes. If several classes are logically related and share common code, abstract classes are a good choice.
  • Use an interface when you want to define a common behavior across unrelated classes. Interfaces are ideal for situations where you need to ensure that a class follows a particular protocol or contract.

Conclusion

Understanding the differences and nuances between abstract classes and interfaces is essential for writing flexible, maintainable Java code. While abstract classes provide a way to share common code and behavior across a hierarchy of related classes, interfaces allow us to define behavior across unrelated classes, making them a powerful tool in Java’s toolkit.

Java’s lack of multiple inheritance is overcome through interfaces, which allow a class to implement multiple behaviors without the ambiguity and complexity that multiple inheritance in other languages may bring.

By mastering these concepts, you’ll be better equipped to design robust, modular applications in Java, making your code more efficient, readable, and scalable.

--

--

@Harsh
@Harsh

Written by @Harsh

A devOps engineer from India

Responses (2)