backend

9 min read

Abstract Class vs Interface in Java (When to Use Each)

Abstract class or interface? Most Java devs get this wrong. Here's a clear breakdown with a side-by-side comparison table, code examples, and a simple decision rule.

Abstract Class vs Interface in Java (When to Use Each) thumbnail

Published By: Nelson Djalo | Date: April 5, 2026

Table of Contents

The One-Sentence Difference

An abstract class says "these things are related and share some behavior." An interface says "these things can do this action, regardless of what they are."

That's really it. Everything else flows from this idea.

A Dog and a Cat both extend Animal because they are animals. But a Dog, a Robot, and a Drone might all implement Trackable because they can be tracked - even though they have nothing else in common.

If you've been writing Java for a while and still second-guess this decision, you're not alone. Let's make it click with real code.

Abstract Class Example

Let's say you're building a payment system. You have credit cards, PayPal, and bank transfers. They all share some common state and behavior:

public abstract class PaymentMethod {

    private String ownerName;
    private boolean verified;

    public PaymentMethod(String ownerName) {
        this.ownerName = ownerName;
        this.verified = false;
    }

    // Every payment method must implement this differently
    public abstract void processPayment(double amount);

    // Shared behavior - same for all payment methods
    public void verify() {
        // common verification logic
        this.verified = true;
        System.out.println(ownerName + "'s payment method verified.");
    }

    public boolean isVerified() {
        return verified;
    }

    public String getOwnerName() {
        return ownerName;
    }
}

Now a concrete class extends it:

public class CreditCard extends PaymentMethod {

    private String cardNumber;

    public CreditCard(String ownerName, String cardNumber) {
        super(ownerName);
        this.cardNumber = cardNumber;
    }

    @Override
    public void processPayment(double amount) {
        System.out.println("Charging $" + amount + " to card " + cardNumber);
    }
}

Notice what the abstract class gives us: a constructor, private fields, a mix of abstract and concrete methods. Subclasses inherit real state and behavior. This is the kind of thing interfaces simply cannot do in the same way.

If you're just getting started with Java and want to build this kind of thing from scratch, the Java for Beginners course walks you through OOP step by step.

Interface Example

Now let's say some things in your system need to be refundable. Not just payment methods - maybe subscriptions, orders, and gift cards too. They have nothing else in common. Perfect use case for an interface:

public interface Refundable {

    void refund(double amount);

    boolean isEligibleForRefund();
}

Any class can implement it:

public class Order implements Refundable {

    private double total;
    private boolean shipped;

    public Order(double total) {
        this.total = total;
        this.shipped = false;
    }

    @Override
    public void refund(double amount) {
        System.out.println("Refunding $" + amount + " for order.");
    }

    @Override
    public boolean isEligibleForRefund() {
        return !shipped;
    }
}

The key thing: Order doesn't extend some "refundable parent." It just promises it can do refund-related things. That's a contract, not an inheritance relationship.

Side-by-Side Comparison Table

Here's everything in one place:

FeatureAbstract ClassInterface
InstantiationCannot be instantiated directlyCannot be instantiated directly
ConstructorsYes, can have constructorsNo constructors
FieldsInstance fields (any type, mutable state)Only public static final constants
MethodsAbstract + concrete methodsAbstract, default, static, private (Java 9+)
InheritanceSingle inheritance only (extends one class)Multiple inheritance (implements many)
Access modifiersAll access modifiers allowedMethods are public by default
Use caseShared state and behavior among related classesDefining capabilities across unrelated classes

The biggest practical difference? A class can implement 10 interfaces but can only extend one abstract class. That constraint alone drives most design decisions.

Using Both Together

This is where it gets interesting. In real projects you almost always combine both. Here's our CreditCard extending an abstract class and implementing an interface:

public class CreditCard extends PaymentMethod implements Refundable {

    private String cardNumber;

    public CreditCard(String ownerName, String cardNumber) {
        super(ownerName);
        this.cardNumber = cardNumber;
    }

    @Override
    public void processPayment(double amount) {
        System.out.println("Charging $" + amount + " to card " + cardNumber);
    }

    @Override
    public void refund(double amount) {
        System.out.println("Refunding $" + amount + " to card " + cardNumber);
    }

    @Override
    public boolean isEligibleForRefund() {
        return isVerified();
    }
}

CreditCard is a PaymentMethod (abstract class) and can do refunds (interface). Both relationships are clear and they serve different purposes.

This pattern shows up everywhere in professional Java code. The Java Master Class dives deep into designing these kinds of hierarchies for real-world applications.

Default Methods in Interfaces (Java 8+)

Before Java 8, interfaces could only have abstract methods. That changed with default methods:

public interface Refundable {

    void refund(double amount);

    boolean isEligibleForRefund();

    // Default method - provides a body, but can be overridden
    default String getRefundPolicy() {
        return "Standard 30-day refund policy.";
    }
}

Any class implementing Refundable gets getRefundPolicy() for free. It can override it if needed, but it doesn't have to.

This blurred the line between abstract classes and interfaces. So here's the updated rule of thumb:

  • Use default methods when you need to add new behavior to an existing interface without breaking all implementors.
  • Use abstract classes when you need constructors, mutable state, or non-public methods shared across subclasses.

Default methods were added mainly for backward compatibility (think List.forEach() in the Collections API). They're not a replacement for abstract classes. If your "interface" needs a constructor and five fields, it's an abstract class in disguise.

Sealed Interfaces (Java 17+)

Java 17 introduced sealed types, and they work with both classes and interfaces. A sealed interface restricts which classes can implement it:

public sealed interface Shape
        permits Circle, Rectangle, Triangle {

    double area();
}

public record Circle(double radius) implements Shape {
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

public record Rectangle(double width, double height) implements Shape {
    @Override
    public double area() {
        return width * height;
    }
}

public record Triangle(double base, double height) implements Shape {
    @Override
    public double area() {
        return 0.5 * base * height;
    }
}

Why does this matter? Two big reasons:

  1. The compiler knows all possible implementations. This makes pattern matching with switch exhaustive - no need for a default case.
  2. You control your API. No random class from another package can implement your Shape interface and break your assumptions.
public double describe(Shape shape) {
    return switch (shape) {
        case Circle c    -> "Circle with radius " + c.radius();
        case Rectangle r -> "Rectangle " + r.width() + "x" + r.height();
        case Triangle t  -> "Triangle with base " + t.base();
        // No default needed - compiler knows this is exhaustive
    };
}

Sealed interfaces pair beautifully with records and pattern matching. If you're building with Java 17+, this is a pattern you'll want in your toolkit. For a deeper look at how inheritance works under the hood, check out Inheritance in Java.

The Simple Decision Rule

When you're staring at your code wondering which one to pick, ask yourself two questions:

1. Do the classes share state (fields) or need a constructor? Yes -> Abstract class.

2. Are you defining a capability that unrelated classes might need? Yes -> Interface.

If both are true (which happens often), use both. Abstract class for the shared foundation, interface for the cross-cutting capabilities.

Here's one more angle: if you're designing something and you think "I might need to add a second parent later" - that's an interface. Java's single inheritance means you get one shot with extends, so save it for the relationship that truly represents an "is-a" hierarchy.

For a bigger picture of how these patterns fit into building real applications, the Spring Boot Roadmap shows how interfaces drive dependency injection and inversion of control in Spring.

FAQ

Can an abstract class implement an interface?

Yes. This is actually common. An abstract class can implement an interface and provide default implementations for some (or all) of the interface's methods. Subclasses then fill in the rest or override as needed.

public abstract class AbstractOrder implements Refundable {
    // Implement one method, leave the other abstract
    @Override
    public boolean isEligibleForRefund() {
        return true;
    }
}

When should I use an abstract class over an interface?

Use an abstract class when your subclasses need to share actual state (instance variables) and you want to enforce initialization through constructors. If you just need to define a contract without shared state, an interface is the better choice.

Can an interface extend another interface?

Yes, and an interface can extend multiple interfaces. This is how you compose contracts:

public interface Purchasable extends Refundable, Shippable {
    void purchase();
}

Any class implementing Purchasable must implement all methods from Refundable, Shippable, and Purchasable.

What happens if a class implements two interfaces with the same default method?

The compiler forces you to override the conflicting method. You can choose which default to call using InterfaceName.super.methodName(), or you can write your own implementation entirely.

public class MyClass implements InterfaceA, InterfaceB {
    @Override
    public void conflictingMethod() {
        InterfaceA.super.conflictingMethod(); // pick one
    }
}

Are abstract classes slower than interfaces?

No. In modern JVMs, the performance difference is negligible. The JIT compiler optimizes virtual method dispatch for both. Pick based on design, not performance. You'll never notice a difference in real applications.

Conclusion

Abstract classes share state and behavior among related types. Interfaces define capabilities that any class can adopt. Use abstract classes for "is-a" relationships with shared fields, and interfaces for "can-do" contracts across unrelated classes. When in doubt, start with an interface - you can always extract an abstract class later if shared state emerges.

Want to put this into practice? The Java Master Class has hands-on projects where you'll design class hierarchies, implement interfaces, and build real applications from scratch.

Your Career Transformation Starts Now

Join thousands of developers mastering in-demand skills with Amigoscode. Try it free today.