Menu
Coddy logo textTech

Abstract Classes vs Interfaces

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 38 of 87.

Now that you understand both abstract classes and interfaces, a common question arises: when should you use one over the other? The choice depends on what you're trying to model.

Use an abstract class when classes share a common base with shared state or behavior. Abstract classes can have instance variables, constructors, and a mix of abstract and concrete methods:

public abstract class Animal {
    protected String name;  // shared state
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // shared behavior
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // must be implemented
}

Use an interface when you want to define a capability that unrelated classes can share. Interfaces focus on what an object can do, not what it is:

public interface Flyable {
    void fly();
}

// Unrelated classes can share this capability
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }

Here's a quick comparison:

FeatureAbstract ClassInterface
Instance variablesYesOnly constants
ConstructorsYesNo
Multiple inheritanceNo (single extends)Yes (multiple implements)
Access modifiersAnyPublic only (for abstract methods)

A practical guideline: if you find yourself creating an abstract class with only abstract methods and no state, an interface is likely the better choice.

challenge icon

Challenge

Easy

Let's build a vehicle system that demonstrates when to use abstract classes versus interfaces. You'll model vehicles that share common state and behavior through an abstract class, while adding optional capabilities through interfaces.

You'll organize your code across five files:

  • Vehicle.java: Create an abstract class that serves as the foundation for all vehicles. Every vehicle has a brand field (String) and a year field (int). Include a constructor to initialize both fields, getter methods for each, and an abstract method startEngine() that returns a String. Also add a concrete method getInfo() that returns: [brand] ([year]). This is a perfect use case for an abstract class—vehicles share state (brand, year) and some behavior (getInfo), but each starts its engine differently.
  • Convertible.java: Define an interface for vehicles that can convert their roof. This capability isn't tied to what a vehicle is—it's something certain vehicles can do. Declare two methods: openRoof() and closeRoof(), both returning String.
  • Car.java: Create a class that extends Vehicle and implements Convertible. A Car has an additional numDoors field (int). Use super to initialize the inherited fields. Implement startEngine() to return: [brand] car engine started. Implement openRoof() to return: [brand] roof opening and closeRoof() to return: [brand] roof closing.
  • Motorcycle.java: Create a class that extends Vehicle but does NOT implement Convertible—motorcycles don't have roofs! A Motorcycle has a hasSidecar field (boolean). Implement startEngine() to return: [brand] motorcycle engine roaring.
  • Main.java: Bring everything together to showcase the difference between abstract classes and interfaces. You'll receive four inputs: a car brand, a car year, a motorcycle brand, and a motorcycle year.

    Create a Car (with 4 doors) and a Motorcycle (without sidecar). First, demonstrate the shared abstract class behavior by printing getInfo() for both vehicles—they both inherit this from Vehicle. Then print the result of startEngine() for each—notice how each vehicle type implements this differently.

    Finally, demonstrate the interface capability: since only the Car implements Convertible, call and print openRoof() followed by closeRoof() on the car only.

You will receive four inputs: car brand (String), car year (int), motorcycle brand (String), and motorcycle year (int).

Your output should show six lines total. Notice how both vehicles share the abstract class's state and getInfo() method, but only the Car has the convertible capability—this illustrates when to use each approach!

Cheat sheet

Use an abstract class when classes share a common base with shared state or behavior. Abstract classes can have instance variables, constructors, and a mix of abstract and concrete methods:

public abstract class Animal {
    protected String name;  // shared state
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // shared behavior
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // must be implemented
}

Use an interface when you want to define a capability that unrelated classes can share. Interfaces focus on what an object can do, not what it is:

public interface Flyable {
    void fly();
}

// Unrelated classes can share this capability
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }
FeatureAbstract ClassInterface
Instance variablesYesOnly constants
ConstructorsYesNo
Multiple inheritanceNo (single extends)Yes (multiple implements)
Access modifiersAnyPublic only (for abstract methods)

Guideline: If you're creating an abstract class with only abstract methods and no state, an interface is likely the better choice.

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Read inputs
        String carBrand = scanner.nextLine();
        int carYear = scanner.nextInt();
        scanner.nextLine(); // consume newline
        String motorcycleBrand = scanner.nextLine();
        int motorcycleYear = scanner.nextInt();
        
        // TODO: Create a Car with 4 doors
        
        // TODO: Create a Motorcycle without sidecar (false)
        
        // TODO: Print getInfo() for both vehicles (demonstrates shared abstract class behavior)
        
        // TODO: Print startEngine() for both vehicles (demonstrates different implementations)
        
        // TODO: Print openRoof() and closeRoof() for car only (demonstrates interface capability)
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming