Module: Inheritance and Polymorphism

Interfaces and Abstract Classes

Java Core: Inheritance and Polymorphism - Interfaces and Abstract Classes

This document outlines the concepts of Interfaces and Abstract Classes in Java, crucial for understanding inheritance and polymorphism.


1. Abstract Classes

What is an Abstract Class?

An abstract class is a class that cannot be instantiated directly. It's designed to be a blueprint for other classes. It can contain both abstract and concrete (fully implemented) methods.

Key Characteristics:

  • abstract keyword: Declared using the abstract keyword. public abstract class MyAbstractClass { ... }
  • Abstract Methods: Methods declared without a body (implementation). They must be implemented by concrete subclasses. public abstract void myMethod();
  • Concrete Methods: Methods with a full implementation. Subclasses can use these directly or override them.
  • Can have constructors: Although you can't create instances of an abstract class, constructors are used by subclasses during initialization.
  • Can have instance variables: Abstract classes can hold data.
  • Single Inheritance: Java only supports single inheritance for classes. A class can only extend one abstract class.

When to use Abstract Classes:

  • Common functionality: When you want to provide a common base for a group of related classes, sharing some common methods and data.
  • Partial implementation: When you want to define a template for subclasses, forcing them to implement certain methods while providing some default behavior.
  • "Is-a" relationship: Abstract classes represent a more general concept, and subclasses represent more specific types of that concept (e.g., Animal is abstract, Dog and Cat extend it).

Example:

abstract class Shape {
    String color;

    Shape(String color) {
        this.color = color;
    }

    // Abstract method - must be implemented by subclasses
    public abstract double area();

    // Concrete method - can be used or overridden
    public String getColor() {
        return color;
    }
}

class Circle extends Shape {
    double radius;

    Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

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

class Rectangle extends Shape {
    double width;
    double height;

    Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }

    @Override
    public double area() {
        return width * height;
    }
}

public class AbstractClassExample {
    public static void main(String[] args) {
        // Shape shape = new Shape(); // Error: Cannot instantiate abstract class
        Circle circle = new Circle("Red", 5.0);
        Rectangle rectangle = new Rectangle("Blue", 4.0, 6.0);

        System.out.println("Circle Area: " + circle.area());
        System.out.println("Rectangle Area: " + rectangle.area());
    }
}

2. Interfaces

What is an Interface?

An interface is a completely abstract "contract" that specifies a set of methods that a class must implement. It defines what a class should do, but not how it should do it.

Key Characteristics:

  • interface keyword: Declared using the interface keyword. public interface MyInterface { ... }
  • Abstract Methods (by default): All methods declared in an interface are implicitly public and abstract. You don't need to explicitly use these keywords (though you can).
  • No Instance Variables (until Java 8): Interfaces traditionally couldn't have instance variables (fields). (See Java 8 updates below).
  • Multiple Inheritance: A class can implement multiple interfaces. This is a key difference from abstract classes.
  • Default Methods (Java 8+): Java 8 introduced the ability to define default methods in interfaces, providing a default implementation that classes can choose to override.
  • Static Methods (Java 8+): Java 8 also allows static methods in interfaces. These are called using the interface name (e.g., MyInterface.staticMethod()).
  • Private Methods (Java 9+): Java 9 introduced private methods in interfaces to help with code reuse within the interface itself.

When to use Interfaces:

  • Defining a role: When you want to define a capability or role that multiple unrelated classes can fulfill (e.g., Runnable, Comparable).
  • Multiple inheritance: When you need a class to exhibit behaviors from multiple sources.
  • Loose coupling: Interfaces promote loose coupling between classes, making your code more flexible and maintainable.
  • "Can-do" relationship: Interfaces represent a capability that a class can have (e.g., a Bird can Fly).

Example:

interface Flyable {
    void fly(); // Abstract method
}

interface Swimmable {
    void swim(); // Abstract method
}

class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("Duck is flying.");
    }

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

class Fish implements Swimmable {
    @Override
    public void swim() {
        System.out.println("Fish is swimming.");
    }
}

public class InterfaceExample {
    public static void main(String[] args) {
        Duck duck = new Duck();
        Fish fish = new Fish();

        duck.fly();
        duck.swim();
        fish.swim();
        // fish.fly(); // Error: Fish does not implement Flyable
    }
}

Java 8+ Interface Enhancements:

interface MyInterface {
    // Abstract method
    void abstractMethod();

    // Default method (provides a default implementation)
    default void defaultMethod() {
        System.out.println("Default implementation");
    }

    // Static method
    static void staticMethod() {
        System.out.println("Static method in interface");
    }
}

3. Abstract Class vs. Interface: Key Differences

Feature Abstract Class Interface
Keywords abstract class interface
Methods Can have abstract and concrete methods Traditionally only abstract methods (Java 8+ allows default and static)
Instance Variables Can have instance variables Traditionally no instance variables (Java 8+ allows private static final variables)
Inheritance Single inheritance (extends only one abstract class) Multiple inheritance (implements multiple interfaces)
"Is-a" vs. "Can-do" Represents an "is-a" relationship Represents a "can-do" relationship
Implementation Provides partial implementation Defines a contract; no implementation (except default methods in Java 8+)
Flexibility Less flexible due to single inheritance More flexible due to multiple inheritance

4. Choosing Between Abstract Classes and Interfaces

  • Use an Abstract Class:

    • When you have a clear "is-a" relationship.
    • When you want to share some common implementation details among subclasses.
    • When you anticipate that the class hierarchy will evolve and require adding more methods in the future.
  • Use an Interface:

    • When you want to define a role or capability that multiple unrelated classes can fulfill.
    • When you need multiple inheritance.
    • When you want to promote loose coupling and flexibility.
    • When you want to define a contract without providing any implementation.

This comprehensive overview should provide a solid understanding of abstract classes and interfaces in Java. Remember to practice with examples to solidify your knowledge.