Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 15

Interfaces vs. Abstract Classes

Recommended resources

Welcome back! In our previous lesson, we explored abstract classes, which act as non-instantiable blueprints for a family of related classes. We saw how an abstract Animal class can enforce that all its subclasses, like Dog and Cat, must implement a makeNoise method. This established a strong "is-a" relationship (A Dog is an Animal).

This lesson introduces a complementary concept: interfaces. What happens when you want to define a common capability for classes that might be completely unrelated? For example, a Bird and an Airplane can both fly, but they don't share a common parent in the way a Dog and Cat do. Interfaces solve this by defining a contract of behaviors—a "can-do" relationship.

By the end of this lesson, you will be able to define and implement interfaces in Java and, crucially, understand when to use an interface versus when to use an abstract class, a common point of confusion and a frequent topic in technical interviews.

1. The Need for Interfaces: Decoupling Your Code

Before diving into the syntax, it's essential to understand why interfaces are so powerful. They are a primary tool for achieving loose coupling in software design, which means reducing the dependencies between different parts of your system. A loosely coupled system is more flexible, easier to maintain, and easier to test.

The Telusko YouTube channel has an excellent video that builds a scenario demonstrating this exact need. It starts with a Developer class that is tightly coupled to a Laptop class and shows how this design breaks when a Desktop is introduced.

#66 Need of Interface in Java

This video tells a story that perfectly illustrates the problem that interfaces solve. It will guide you from a rigid, tightly-coupled design to a flexible one.

Watch these segments to see the problem and the evolution of the solution: The Problem of Tight Coupling: The instructor sets up a scenario where a Developer object can only work with a Laptop object. Notice the error when they try to give the developer a Desktop. This is the core problem we want to solve. Solution with an Abstract Class: Here, an abstract Computer class is introduced to generalize Laptop and Desktop. This decouples the Developer from a specific implementation, which is a big improvement and links directly to our last lesson. Introducing Interfaces: Finally, the video shows how an interface can achieve the same goal, setting the stage for our main topic.

As the video demonstrates, both abstract classes and interfaces can be used to write more flexible code. The Developer doesn't care if it's a Laptop or a Desktop; it only cares that it has a Computer that can code(). Now, let's formalize the concept of an interface.


2. Defining and Implementing an Interface

An interface is a purely abstract type that defines a contract. It specifies a set of methods that an implementing class must provide.

Here's the basic syntax:

// 1. Defining the interface
public interface Drivable {
    // Methods in an interface are public and abstract by default.
    // You don't need to write 'public abstract'.
    void startEngine();
    void turn(String direction);
    void stopEngine();
}

// 2. A class implementing the interface
public class Car implements Drivable {
    
    // The class MUST provide an implementation for all methods in the interface.
    @Override
    public void startEngine() {
        System.out.println("Car engine started.");
    }

    @Override
    public void turn(String direction) {
        System.out.println("Car is turning " + direction);
    }

    @Override
    public void stopEngine() {
        System.out.println("Car engine stopped.");
    }
}

// 3. Another unrelated class can also implement the same interface
public class LawnMower implements Drivable {

    @Override
    public void startEngine() {
        System.out.println("Lawnmower engine started with a pull cord.");
    }

    @Override
    public void turn(String direction) {
        System.out.println("Pushing the lawnmower to turn " + direction);
    }

    @Override
    public void stopEngine() {
        System.out.println("Lawnmower engine stopped.");
    }
}

Key characteristics of an interface:

  • You use the interface keyword to declare it.
  • A class uses the implements keyword to adopt the contract.
  • All methods are implicitly public and abstract.
  • Any fields declared in an interface are implicitly public, static, and final (they are constants). This means interfaces cannot have state (instance variables).

The article "When to use abstract classes vs. interfaces in Java" from InfoWorld provides a clear example of implementing an interface.

When to use abstract classes vs. interfaces in Java - InfoWorld

This article gets straight to the point about how interfaces work as a contract.

Read the section on using interfaces, which uses the List interface from the JDK as a great example of polymorphism. Then, review the example under Overriding an interface method to see the implements and @Override keywords in action.


3. Abstract Class vs. Interface: The Decisive Comparison

This is the central question. While they seem similar, they have fundamental differences that dictate their use cases.

Core Distinction: "Is-A" vs. "Can-Do"

  • Abstract Class (is-a): Use an abstract class when you have a group of tightly related classes that share a common identity. An Audi is a Car. A Car is a Vehicle. This defines a hierarchy.
  • Interface (can-do): Use an interface to define a capability that can be shared by unrelated classes. A Car can drive. A Robot can drive. A Bird can fly. An Airplane can fly. This defines a contract for a behavior.

The Baeldung article "Using an Interface vs. Abstract Class in Java" provides excellent heuristics for this.

Using an Interface vs. Abstract Class in Java

This article provides clear, actionable guidance on when to choose one over the other.

Focus on these two sections: "When to Use an Interface": Pay close attention to the bolded sentence: "A is capable of [doing this]". "When to Use an Abstract Class": Note the corresponding heuristic here: "A is a B".

Key Technical Differences

Let's break down the technical distinctions.

  1. Multiple Inheritance: A class can implement multiple interfaces, but can only extend one class (abstract or not). This is a major reason to prefer interfaces when a class needs to take on several roles.
    class FlyingCar extends Car implements Flyable, Drivable { ... }

  2. State (Instance Fields): Abstract classes can have instance variables (state) with any access modifier (private, protected, etc.). Interfaces can only have public static final constants. If you need to share state or complex implementation details among subclasses, an abstract class is the right tool.

  3. Constructors: Abstract classes have constructors, which are called by their subclasses using super(). Interfaces do not have constructors.

  4. Methods:

    • Abstract Classes can have a mix of abstract methods (no body) and concrete methods (with implementation).
    • Interfaces (traditionally) only had abstract methods. However, Java 8 introduced default and static methods.
      • default methods provide an implementation within the interface. This allows new methods to be added to an interface without breaking all the existing classes that implement it.
      • static methods are utility methods that are part of the interface but are not inherited by implementing classes.

The video from Coding with John offers a concise verbal and visual comparison of these points.

Abstract Classes and Methods in Java Explained in 7 Minutes

This segment directly addresses the common interview question: "What's the difference between an abstract class and an interface?"

Watch from the start of the comparison. The video clearly explains the difference in multiple inheritance and how fields are handled in each.


4. Visual Summary

Let's consolidate our understanding with some visuals. The first image provides a side-by-side code example of an abstract class versus an interface.

This image shows the syntactic differences between an abstract class `Animal` (using `abstract class` and `extends`) and an interface `IAnimal` (using `interface` and `implements`).

The second image is a comprehensive feature comparison table, which serves as an excellent cheat sheet.

This table summarizes the key features of interfaces and abstract classes, covering constructors, types of fields and methods, and access modifiers. It's a perfect reference for quickly recalling the differences.

Conclusion

You've now covered two of the most important concepts in object-oriented design: abstract classes and interfaces. Understanding the trade-offs between them is crucial for writing clean, maintainable, and flexible Java code.

Here are the key takeaways for making the right choice:

  • Choose an abstract class when:

    • You want to share code among several closely related classes (an "is-a" relationship).
    • You need to share state (non-static, non-final fields) or methods with non-public access.
    • You are creating a base for a component that will have a common, partially implemented behavior.
  • Choose an interface when:

    • You need to define a contract for a capability that can be adopted by unrelated classes (a "can-do" relationship).
    • You want a class to inherit behaviors from multiple sources (multiple inheritance of type).
    • You want to design for loose coupling, programming to a contract rather than an implementation (e.g., List list = new ArrayList();).

With this lesson, we conclude our deep dive into the core principles of Object-Oriented Programming. You now have the foundational tools to design robust class structures. In our next module, we will shift our focus from language structure to the powerful libraries that come with Java, starting with how to effectively work with text using the String class.