VisualStudioTutor.com Java Tutorial All Lessons

Lesson 17 of 20

Abstract Classes and Interfaces

Define shared contracts and partial implementations using abstract classes and interfaces.

Learning objectives

  • Create an abstract class and abstract method.
  • Create and implement an interface.
  • Understand when shared state belongs in an abstract class.
  • Use interfaces to model capabilities.
  • Program against a common contract.

1. Abstract classes

An abstract class cannot be instantiated directly. It can hold fields, constructors, concrete methods, and abstract methods that subclasses must implement.

Example: abstract Shape

public abstract class Shape {
    private final String name;

    public Shape(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public abstract double area();
}

Concrete subclass

public class Circle extends Shape {
    private final double radius;

    public Circle(double radius) {
        super("Circle");
        this.radius = radius;
    }

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

2. Interfaces

An interface defines a contract that implementing classes agree to provide. Interfaces are especially useful for capabilities that may be shared by otherwise unrelated classes.

Example: Printable contract

public interface Printable {
    void print();
}

public class Invoice implements Printable {
    @Override
    public void print() {
        System.out.println("Printing invoice...");
    }
}

3. Abstract class or interface?

SituationPreferReason
Need shared instance state or a constructorAbstract classAn abstract class can store fields and initialize them.
Need a capability shared across unrelated classesInterfaceMany different classes can implement the same contract.
Need multiple contractsInterfaceA class can implement multiple interfaces.
Need a common base implementationAbstract classConcrete methods can be inherited and reused.

Mini project: payment processing

public interface PaymentMethod {
    boolean pay(double amount);
}

public class CardPayment implements PaymentMethod {
    @Override
    public boolean pay(double amount) {
        System.out.printf("Charging card: RM %.2f%n", amount);
        return true;
    }
}

public class EWalletPayment implements PaymentMethod {
    @Override
    public boolean pay(double amount) {
        System.out.printf("Charging e-wallet: RM %.2f%n", amount);
        return true;
    }
}

public class Checkout {
    public static void processPayment(PaymentMethod method, double amount) {
        if (method.pay(amount)) {
            System.out.println("Payment successful.");
        }
    }

    public static void main(String[] args) {
        processPayment(new CardPayment(), 120.00);
        processPayment(new EWalletPayment(), 55.50);
    }
}

Checkout depends on the PaymentMethod interface rather than one specific payment class. This makes the design easier to extend.

Common mistakes

ProblemWhy it happensHow to fix it
Trying to instantiate an abstract classAbstract types are incomplete by design.Instantiate a concrete subclass.
Using extends for an interfaceClasses implement interfaces.Write implements InterfaceName.
Interface contains unrelated responsibilitiesThe contract is too broad.Split it into smaller capability-focused interfaces.

Summary

Abstract classes provide a shared base with optional state and implementation; interfaces define contracts that many classes can fulfill. Lesson 18 turns to runtime failures and how Java handles them with exceptions.