Differences with Abstract Classes

Introduction

Abstract classes and interfaces both provide abstraction but have different features and use cases.


Quick Comparison

FeatureAbstract ClassInterface
Keywordabstract classinterface
MethodsAbstract + ConcreteAbstract (+ default in Java 8+)
VariablesAny typepublic static final only
ConstructorYesNo
InheritanceSingle (extends)Multiple (implements)
Access modifiersAllpublic only
FieldsInstance variablesConstants only
When to useIS-A + shared codeCAN-DO capability

Abstract Class Example

abstract class Animal {
    String name;  // Instance variable

    // Constructor
    Animal(String name) {
        this.name = name;
    }

    // Concrete method
    void sleep() {
        System.out.println(name + " is sleeping");
    }

    // Abstract method
    abstract void sound();
}

class Dog extends Animal {
    Dog(String name) {
        super(name);
    }

    @Override
    void sound() {
        System.out.println(name + " barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("Buddy");
        dog.sound();  // Buddy barks
        dog.sleep();  // Buddy is sleeping
    }
}

Interface Example

interface Animal {
    // Constant
    int LEGS = 4;

    // Abstract methods
    void sound();
    void eat();
}

class Dog implements Animal {
    String name;

    Dog(String name) {
        this.name = name;
    }

    @Override
    public void sound() {
        System.out.println(name + " barks");
    }

    @Override
    public void eat() {
        System.out.println(name + " eats food");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("Buddy");
        dog.sound();  // Buddy barks
        dog.eat();    // Buddy eats food
        System.out.println("Legs: " + Animal.LEGS);
    }
}

Methods

Abstract Class:

abstract class Example {
    // Abstract method
    abstract void method1();

    // Concrete method
    void method2() {
        System.out.println("Implemented method");
    }

    // Static method
    static void method3() {
        System.out.println("Static method");
    }

    // Final method
    final void method4() {
        System.out.println("Cannot override");
    }
}

Interface:

interface Example {
    // Abstract method (implicit)
    void method1();

    // Default method (Java 8+)
    default void method2() {
        System.out.println("Default method");
    }

    // Static method (Java 8+)
    static void method3() {
        System.out.println("Static method");
    }
}

Variables

Abstract Class:

abstract class Example {
    // Instance variable
    int x = 10;

    // Static variable
    static int y = 20;

    // Final variable
    final int z = 30;

    // Can be private, protected, public
    private int a = 1;
    protected int b = 2;
    public int c = 3;
}

Interface:

interface Example {
    // All variables are public static final
    int X = 10;  // public static final (implicit)

    // Cannot have:
    // private int a = 1;    // ✗ Error
    // protected int b = 2;  // ✗ Error
    // int c;                // ✗ Must initialize
}

Constructor

Abstract Class:

abstract class Animal {
    String name;

    // Has constructor
    Animal(String name) {
        this.name = name;
        System.out.println("Animal constructor");
    }
}

class Dog extends Animal {
    Dog(String name) {
        super(name);  // Call parent constructor
    }
}

Interface:

interface Animal {
    // No constructor allowed
    // Animal() { }  // ✗ Error
}

class Dog implements Animal {
    // Own constructor
    Dog() {
        System.out.println("Dog constructor");
    }
}

Inheritance

Abstract Class (Single):

abstract class Animal { }
abstract class Mammal { }

// ✗ Cannot extend multiple classes
// class Dog extends Animal, Mammal { }

// ✓ Only one
class Dog extends Animal { }

Interface (Multiple):

interface Walkable { }
interface Swimmable { }
interface Flyable { }

// ✓ Can implement multiple interfaces
class Duck implements Walkable, Swimmable, Flyable { }

Complete Comparison Example

// Abstract Class
abstract class Vehicle {
    String brand;  // Instance variable

    Vehicle(String brand) {  // Constructor
        this.brand = brand;
    }

    void displayBrand() {  // Concrete method
        System.out.println("Brand: " + brand);
    }

    abstract void start();  // Abstract method
}

// Interface
interface Drivable {
    int MAX_SPEED = 200;  // Constant

    void drive();  // Abstract method
    void stop();   // Abstract method
}

// Class using both
class Car extends Vehicle implements Drivable {
    Car(String brand) {
        super(brand);
    }

    @Override
    void start() {
        System.out.println(brand + " car starting");
    }

    @Override
    public void drive() {
        System.out.println(brand + " car driving");
    }

    @Override
    public void stop() {
        System.out.println(brand + " car stopping");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car("Toyota");
        car.displayBrand();  // From abstract class
        car.start();         // Implemented abstract method
        car.drive();         // From interface
        car.stop();          // From interface
        System.out.println("Max speed: " + Drivable.MAX_SPEED);
    }
}

When to Use Abstract Class?

Use when:

  1. Shared code among related classes
  2. Common state (instance variables)
  3. Constructor needed
  4. IS-A relationship (Dog IS-A Animal)
  5. Single inheritance is enough
abstract class Employee {
    protected String name;
    protected double salary;

    Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    void displayInfo() {
        System.out.println(name + ": $" + salary);
    }

    abstract double calculateBonus();
}

class Manager extends Employee {
    Manager(String name, double salary) {
        super(name, salary);
    }

    @Override
    double calculateBonus() {
        return salary * 0.2;
    }
}

When to Use Interface?

Use when:

  1. Multiple inheritance needed
  2. Unrelated classes share behavior
  3. Contract/Capability (CAN-DO)
  4. No shared state needed
  5. Loose coupling desired
interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

// Bird can fly
class Bird implements Flyable {
    public void fly() {
        System.out.println("Bird flying");
    }
}

// Duck can both fly and swim
class Duck implements Flyable, Swimmable {
    public void fly() {
        System.out.println("Duck flying");
    }

    public void swim() {
        System.out.println("Duck swimming");
    }
}

// Fish can only swim
class Fish implements Swimmable {
    public void swim() {
        System.out.println("Fish swimming");
    }
}

Using Both Together

abstract class Animal {
    protected String name;

    Animal(String name) {
        this.name = name;
    }

    void sleep() {
        System.out.println(name + " is sleeping");
    }

    abstract void eat();
}

interface Pet {
    void play();
}

interface Guard {
    void protect();
}

class Dog extends Animal implements Pet, Guard {
    Dog(String name) {
        super(name);
    }

    @Override
    void eat() {
        System.out.println(name + " is eating");
    }

    @Override
    public void play() {
        System.out.println(name + " is playing");
    }

    @Override
    public void protect() {
        System.out.println(name + " is protecting");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("Max");
        dog.eat();      // From Animal
        dog.sleep();    // From Animal
        dog.play();     // From Pet interface
        dog.protect();  // From Guard interface
    }
}

Real-World Example

// Abstract class for shared employee data and behavior
abstract class Employee {
    protected String id;
    protected String name;
    protected double salary;

    Employee(String id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    void displayBasicInfo() {
        System.out.println("ID: " + id);
        System.out.println("Name: " + name);
        System.out.println("Salary: $" + salary);
    }

    abstract double calculateBonus();
}

// Interfaces for capabilities
interface Manageable {
    void manage();
}

interface Teachable {
    void teach();
}

interface Programmable {
    void program();
}

// Manager can manage
class Manager extends Employee implements Manageable {
    Manager(String id, String name, double salary) {
        super(id, name, salary);
    }

    @Override
    double calculateBonus() {
        return salary * 0.3;
    }

    @Override
    public void manage() {
        System.out.println(name + " is managing team");
    }
}

// Developer can program and teach
class Developer extends Employee implements Programmable, Teachable {
    Developer(String id, String name, double salary) {
        super(id, name, salary);
    }

    @Override
    double calculateBonus() {
        return salary * 0.2;
    }

    @Override
    public void program() {
        System.out.println(name + " is programming");
    }

    @Override
    public void teach() {
        System.out.println(name + " is teaching");
    }
}

public class Main {
    public static void main(String[] args) {
        Manager mgr = new Manager("M001", "Alice", 50000);
        mgr.displayBasicInfo();
        mgr.manage();
        System.out.println("Bonus: $" + mgr.calculateBonus());

        System.out.println();

        Developer dev = new Developer("D001", "Bob", 40000);
        dev.displayBasicInfo();
        dev.program();
        dev.teach();
        System.out.println("Bonus: $" + dev.calculateBonus());
    }
}

Migration Strategy

Abstract Class to Interface:

  • Remove instance variables
  • Remove constructors
  • Make all methods public
  • Remove concrete methods (or make default)

Interface to Abstract Class:

  • Add instance variables if needed
  • Add constructor
  • Add concrete methods
  • Keep abstract methods

Quick Reference

// Abstract Class
abstract class AbstractExample {
    int x;  // Instance variable

    AbstractExample() { }  // Constructor

    void concreteMethod() { }  // Concrete method
    abstract void abstractMethod();  // Abstract method
}

class Child extends AbstractExample {
    void abstractMethod() { }
}

// Interface
interface InterfaceExample {
    int X = 10;  // Constant

    void method();  // Abstract method
}

class Implementer implements InterfaceExample {
    public void method() { }
}

// Both
class Combined extends AbstractExample implements InterfaceExample {
    void abstractMethod() { }
    public void method() { }
}

Exam Tips

Remember:

  1. Abstract class: partial implementation
  2. Interface: contract/capability
  3. Abstract class has constructor
  4. Interface has only constants
  5. Single inheritance for abstract class
  6. Multiple inheritance for interface
  7. extends for abstract class
  8. implements for interface
  9. Use both when needed
  10. Choose based on IS-A vs CAN-DO

Common Questions:

  • Abstract class vs interface?
  • When to use abstract class?
  • When to use interface?
  • Can class extend both?
  • Difference in variables?
  • Constructor difference?
  • Multiple inheritance?
  • Which is more flexible?
  • Real-world usage scenarios?
  • Can interface extend abstract class?