Develop/JAVA

[Java] 봉인 클래스(Sealed Classes): Java 17의 혁신적인 기능과 예제까지

issuemaker99 2025. 3. 19. 15:25
728x90

오늘은 Java 17에서 정식으로 도입된 '봉인 클래스(Sealed Classes)'에 대해 알아보겠습니다. 이 기능은 객체지향 프로그래밍의 상속 구조를 더욱 효과적으로 제어할 수 있게 해주는 흥미로운 기능입니다.

봉인 클래스란 무엇인가?

봉인 클래스는 클래스 계층 구조를 명시적으로 제한할 수 있게 해주는 Java 17의 새로운 기능입니다. 간단히 말해, 어떤 클래스가 자신을 상속받을 수 있는 하위 클래스들을 제한할 수 있게 해줍니다.

기존 Java에서는 클래스를 final로 선언하여 상속을 완전히 막거나, 아무런 제한 없이 누구나 상속할 수 있게 하는 두 가지 극단적인 선택지만 있었습니다. 봉인 클래스는 이 두 가지 사이의 중간 지점을 제공합니다.

봉인 클래스의 기본 문법

봉인 클래스를 정의하는 기본 문법은 다음과 같습니다:

// 부모 클래스를 sealed로 선언하고 permits 키워드로 허용할 하위 클래스 명시
public sealed class Shape permits Circle, Rectangle, Triangle {
    // 클래스 내용
}

// 허용된 하위 클래스들은 final, sealed, non-sealed 중 하나로 선언해야 함
public final class Circle extends Shape {
    // final: 더 이상 상속 불가
}

public sealed class Rectangle extends Shape permits Square {
    // sealed: 제한된 상속 허용
}

public non-sealed class Triangle extends Shape {
    // non-sealed: 제한 없이 상속 허용
}

// Rectangle의 하위 클래스로 허용된 Square
public final class Square extends Rectangle {
    // 내용
}

 

봉인 클래스의 주요 특징

  1. 명시적 상속 제어: permits 키워드를 사용하여 상속 가능한 하위 클래스를 명시적으로 나열합니다.
  2. 하위 클래스 선언 의무: 봉인 클래스를 상속받는 모든 클래스는 다음 세 가지 중 하나로 선언되어야 합니다:
    • final: 더 이상 상속할 수 없음
    • sealed: 제한된 상속만 허용
    • non-sealed: 제한 없이 상속 허용
  3. 같은 모듈 또는 패키지 내 존재: 기본적으로 봉인 클래스와 그 허용된 하위 클래스들은 동일한 모듈(또는 모듈이 없는 경우 동일 패키지) 내에 있어야 합니다.

봉인 클래스의 실용적 사용 예시

예시 1: 결제 시스템 구현

public sealed abstract class Payment permits CreditCard, DebitCard, BankTransfer, DigitalWallet {
    protected double amount;
    
    public abstract boolean processPayment();
}

public final class CreditCard extends Payment {
    private String cardNumber;
    private String expiryDate;
    
    @Override
    public boolean processPayment() {
        // 신용카드 결제 처리 로직
        return true;
    }
}

public final class DebitCard extends Payment {
    private String cardNumber;
    private String pin;
    
    @Override
    public boolean processPayment() {
        // 직불카드 결제 처리 로직
        return true;
    }
}

public final class BankTransfer extends Payment {
    private String accountNumber;
    
    @Override
    public boolean processPayment() {
        // 계좌이체 처리 로직
        return true;
    }
}

public non-sealed class DigitalWallet extends Payment {
    private String walletId;
    
    @Override
    public boolean processPayment() {
        // 디지털 지갑 결제 처리 로직
        return true;
    }
}

 

예시 2: 패턴 매칭과의 조합

Java 17에서는 봉인 클래스가 패턴 매칭과 함께 사용될 때 그 진가를 발휘합니다:

public sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
}

public final record Circle(double radius) implements Shape {
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

public final record Rectangle(double width, double height) implements Shape {
    @Override
    public double area() {
        return width * height;
    }
}

public final record Triangle(double base, double height) implements Shape {
    @Override
    public double area() {
        return 0.5 * base * height;
    }
}

// 패턴 매칭을 활용한 처리
public void printArea(Shape shape) {
    double area = switch (shape) {
        case Circle c -> c.area();
        case Rectangle r -> r.area();
        case Triangle t -> t.area();
        // Shape이 봉인되어 있으므로 default 케이스가 필요 없음!
    };
    System.out.println("도형의 면적: " + area);
}

 

봉인 클래스의 장점

  1. 타입 안전성 향상: 클래스 계층 구조를 명확하게 정의하고 제한함으로써 타입 안전성이 향상됩니다.
  2. 패턴 매칭 최적화: 패턴 매칭에서 가능한 모든 케이스가 명시적으로 정의되므로, 컴파일러가 망라성(exhaustiveness) 검사를 수행할 수 있습니다.
  3. API 설계 명확화: 라이브러리 개발자가 의도한 상속 구조를 명확하게 표현할 수 있습니다.
  4. 유지보수성 개선: 클래스 계층 구조가 명확하게 정의되어 있어 코드 이해와 유지보수가 쉬워집니다.

실무에서의 적용 사례

봉인 클래스는 다음과 같은 상황에서 특히 유용합니다:

  1. 도메인 모델링: 비즈니스 개념이 명확하게 제한된 유형 집합으로 모델링될 때 (예: 주문 상태, 결제 방법 등)
  2. 라이브러리/프레임워크 설계: 확장 지점을 명확하게 정의해야 할 때
  3. 패턴 매칭 활용: switch 표현식이나 패턴 매칭을 활용한 로직 처리 시
  4. 컴파일러 최적화: 컴파일러가 클래스 계층에 대해 더 많은 가정을 할 수 있게 함으로써 최적화 기회를 제공

주의사항

  1. 모듈/패키지 제약: 봉인 클래스와 그 허용된 하위 클래스들은 기본적으로 같은 모듈이나 패키지 내에 있어야 합니다.
  2. 하위 클래스 제약: 모든 하위 클래스는 final, sealed, 또는 non-sealed로 선언되어야 합니다.
  3. IDE 지원: 최신 IDE를 사용하여 봉인 클래스 관련 기능을 충분히 활용하세요.

 

LIST