Java Sealed Class vs Final Class

1. Introduction

This blog post examines the differences between sealed classes and final classes in Java. While both concepts relate to class inheritance, they serve different purposes. Understanding these differences is crucial for Java developers, especially when designing class hierarchies.

Definition

- A sealed class is a class that can only be extended by a specific set of classes or interfaces, providing a more controlled form of inheritance.

- A final class is a class that cannot be extended at all, ensuring its implementation remains unchanged by subclasses.

2. Key Differences 

1. Extensibility 

Final Class: Cannot be extended by any other class. 

Sealed Class: Can be extended by a specific set of classes. 

2. Use Cases 

Final Class: Used when you want to prevent any modification or extension of a class, often for reasons of security, immutability, or design simplicity. 

Sealed Class: Ideal for situations where you need a closed set of types, such as when implementing a domain model or a finite set of operations in a system. 

3. Flexibility 

Final Class: Offers a rigid structure with no flexibility in terms of class hierarchy. 

Sealed Class: Provides more flexibility, allowing the creation of a controlled and predictable hierarchy. 

4. Design Intent 

Final Class: Indicates that the class is complete in its current form and should not be altered or extended. 

Sealed Class: Suggests a more comprehensive design consideration, where the class is intended to be extended in controlled and predefined ways.

3. Example

1. Create an example of a sealed class with permitted subclasses.

2. Create an example of a final class.

3. Demonstrate how each class type is used.

Here is the complete example to demonstrate the usage of Sealed Class and Final Class:

// Sealed class example
sealed abstract class Shape permits Circle, Square {
    abstract double area();
}

final class Circle extends Shape {
    private final double radius;
    Circle(double radius) { this.radius = radius; }
    @Override double area() { return Math.PI * radius * radius; }
}

non-sealed class Square extends Shape {
    private final double side;
    Square(double side) { this.side = side; }
    @Override double area() { return side * side; }
}

// Final class example
final class ImmutablePoint {
    private final int x, y;
    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }
    // Getters for x and y
}

public class SealedVsFinalClasses {
    public static void main(String[] args) {
        // Using sealed class
        Shape circle = new Circle(5);
        System.out.println("Area of Circle: " + circle.area());

        // Using final class
        ImmutablePoint point = new ImmutablePoint(10, 20);
        System.out.println("Immutable Point: (" + point.x + ", " + point.y + ")");
    }
}

Output:

Area of Circle: 78.53981633974483
Immutable Point: (10, 20)

Explanation:

1. Shape is a sealed class, allowing only certain classes (Circle, Square) to extend it. This ensures control over the inheritance hierarchy.

2. Circle and Square are subclasses of Shape, but Circle is final, meaning no further subclassing is possible.

3. ImmutablePoint is a final class, that cannot be extended, ensuring the integrity and immutability of its instances.

4. In SealedVsFinalClasses, instances of both a sealed subclass (Circle) and a final class (ImmutablePoint) are created and used.

5. The output demonstrates the functionality of both: circle.area() computes the area, and ImmutablePoint provides a fixed point coordinate.

6. This example highlights the differences between sealed and final classes: sealed classes provide controlled inheritance, whereas final classes prevent any inheritance.


Comments