Java > Object-Oriented Programming (OOP) > Abstraction > Abstract Classes

Abstract Class Example: Shape Hierarchy

This example demonstrates abstraction using abstract classes in Java. We define an abstract `Shape` class with common properties and methods. Concrete shape classes (e.g., `Circle`, `Rectangle`) extend the abstract class and provide implementations for the abstract methods. This hides the complexity of shape calculations behind a simple interface.

Code Snippet: Abstract Shape Class and Concrete Implementations

The `Shape` class is declared as `abstract`, meaning it cannot be instantiated directly. It contains an abstract method `getArea()` and `getDescription()` which forces subclasses to provide their specific implementations for calculating the area and description. The `Circle` and `Rectangle` classes extend `Shape` and provide concrete implementations for `getArea()` based on their specific formulas. The `getColor` method is a concrete method that can be inherited by all subclasses.

// Abstract Shape class
abstract class Shape {
    protected String color;

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

    // Abstract method to calculate area (must be implemented by subclasses)
    public abstract double getArea();

    // Concrete method that all shapes inherit
    public String getColor() {
        return color;
    }

    public abstract String getDescription();
}

// Circle class extending Shape
class Circle extends Shape {
    private double radius;

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

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

    @Override
    public String getDescription(){
        return "This is a circle with radius: " + radius + " and color: " + getColor();
    }
}

// Rectangle class extending Shape
class Rectangle extends Shape {
    private double width;
    private double height;

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

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

    @Override
    public String getDescription(){
        return "This is a rectangle with width: " + width + ", height: " + height + " and color: " + getColor();
    }
}

// Example usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle("Red", 5.0);
        Shape rectangle = new Rectangle("Blue", 4.0, 6.0);

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

Concepts Behind the Snippet

  • Abstraction: Abstract classes allow you to define a common interface for a group of related classes without specifying all the implementation details.
  • Abstract Method: A method declared without an implementation. Subclasses must provide the implementation.
  • Inheritance: Subclasses inherit properties and methods from the abstract class, promoting code reuse and consistency.
  • Polymorphism: Allows you to treat objects of different classes in a uniform way (e.g., calling `getArea()` on both `Circle` and `Rectangle`).

Real-Life Use Case

Consider a drawing application. You might have various shapes like circles, squares, triangles, etc. You could define an abstract `Shape` class with methods like `draw()`, `setColor()`, and `getArea()`. Each concrete shape class would then implement the `draw()` method to draw itself on the screen. The application can then treat all shapes as `Shape` objects, simplifying drawing and manipulation logic.

Best Practices

  • Use abstract classes when you want to define a common interface for a group of related classes but some implementation details are specific to each class.
  • Avoid overusing abstract classes. If all the implementation details are known, use a regular class instead.
  • Make sure the abstract methods are well-defined and have a clear purpose.
  • Provide a constructor in the abstract class to initialize common properties.

Interview Tip

Be prepared to explain the difference between abstract classes and interfaces in Java. Abstract classes can have concrete methods, while interfaces cannot (until Java 8). A class can implement multiple interfaces, but it can only extend one abstract class.

When to Use Them

Use abstract classes when you have a clear 'is-a' relationship between classes (e.g., a `Circle` *is a* `Shape`). Also, use them when you want to provide some default behavior in the base class (using concrete methods) while forcing subclasses to implement specific behaviors (using abstract methods).

Memory Footprint

Abstract classes themselves don't directly contribute to memory footprint because they cannot be instantiated. However, the subclasses that extend the abstract class will have a memory footprint based on their own fields and the fields they inherit from the abstract class. The memory footprint will be similar to using a regular class with inheritance.

Alternatives

  • Interfaces: If you only need to define a contract and not provide any implementation details, use an interface. Classes can implement multiple interfaces.
  • Composition: Instead of inheritance, you can use composition, where a class holds an instance of another class as a field. This can provide more flexibility.

Pros

  • Enforces a common interface for related classes.
  • Allows for code reuse through concrete methods in the abstract class.
  • Provides a degree of flexibility in implementation.

Cons

  • A class can only extend one abstract class, limiting inheritance options.
  • Can lead to complex inheritance hierarchies if not designed carefully.

FAQ

  • Can an abstract class have a constructor?

    Yes, abstract classes can have constructors. The constructor is used to initialize the state of the abstract class when a subclass is instantiated. The subclass must call the superclass constructor using `super()`.
  • Can an abstract method be private?

    No, abstract methods cannot be private. Abstract methods must be implemented by subclasses, and private methods are not accessible to subclasses.
  • Can I create an instance of an abstract class?

    No, you cannot directly create an instance of an abstract class. You can only create instances of concrete subclasses that extend the abstract class.
  • What happens if a subclass doesn't implement all abstract methods of an abstract class?

    If a subclass doesn't implement all abstract methods of its abstract superclass, then the subclass must also be declared as abstract.