Menu
Coddy logo textTech

Sealed 클래스 (Java 17+)

Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 52번째.

전통적인 Java에서는 클래스를 생성할 때, (final로 표시하지 않는 한) 다른 어떤 클래스도 이를 상속할 수 있습니다. 하지만 상속을 허용하되, 아무나 상속받지 못하게 하고 싶다면 어떨까요? Java 17은 어떤 클래스가 자신의 클래스를 상속할 수 있는지 정밀하게 제어할 수 있도록 봉인된 클래스(sealed classes)를 도입했습니다.

봉인된 클래스(sealed class)sealedpermits 키워드를 사용하여 허용된 하위 클래스를 명시적으로 선언합니다:

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    double radius;
    double area() { return Math.PI * radius * radius; }
}

final class Rectangle extends Shape {
    double width, height;
    double area() { return width * height; }
}

final class Triangle extends Shape {
    double base, height;
    double area() { return 0.5 * base * height; }
}

Circle, Rectangle, Triangle만이 Shape를 상속(extend)할 수 있습니다. 그 외의 다른 클래스가 이를 상속하려고 시도하면 컴파일 오류가 발생합니다.

허용된 각 하위 클래스는 세 가지 제어자 중 하나를 사용해야 합니다: final (더 이상 확장 불가), sealed (제한 체인을 계속 유지), 또는 non-sealed (제한 없는 확장을 허용):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // 확장할 수 없음
non-sealed class Truck extends Vehicle { }    // 누구나 Truck을 확장할 수 있음

봉인된 클래스(Sealed classes)는 컴파일러가 모든 가능한 하위 타입을 알고 있으며 망라성(exhaustiveness)을 검증할 수 있기 때문에, switch 표현식의 패턴 매칭과 함께 사용할 때 특히 강력합니다. 결제 수단, 응답 타입 또는 기하학적 도형과 같이 도메인 내의 고정된 타입 집합을 모델링할 때 이상적입니다.

challenge icon

챌린지

쉬움

애플리케이션에서 존재할 수 있는 알림 유형을 제한함으로써 봉인된 클래스(sealed classes)를 보여주는 알림 시스템을 구축해 보겠습니다. 특정 알림 유형만 허용되고 각 유형이 전달을 다르게 처리하는 계층 구조를 만들 것입니다.

코드는 다음 네 개의 파일로 구성됩니다:

  • Notification.java: 모든 알림의 기반이 되는 봉인된 추상 클래스(sealed abstract class)를 생성합니다. 이 클래스는 정확히 세 개의 서브클래스인 EmailNotification, SMSNotification, PushNotification만을 허용(permit)해야 합니다. 이 봉인된 클래스에는 보호된 필드인 message (String), 이를 초기화하는 생성자, 메시지에 대한 getter, 그리고 알림이 전송되는 방식을 설명하는 String을 반환하는 추상 메서드 deliver()가 필요합니다.
  • EmailNotification.java: Notification을 상속받는 final 클래스를 생성합니다. EmailNotification은 이메일 주소를 위한 추가적인 프라이빗 필드 recipient (String)를 가집니다. 생성자는 메시지와 수신자(recipient)를 모두 매개변수로 받아야 하며, super()를 사용하여 부모 생성자를 호출해야 합니다. deliver() 메서드는 "Sending email to " + recipient + ": " + message를 반환해야 합니다.
  • SMSNotification.java: Notification을 상속받는 non-sealed 클래스를 생성합니다. 이는 나중에 필요한 경우 다른 클래스가 이를 상속받을 수 있도록 허용합니다. SMSNotification은 추가적인 프라이빗 필드 phoneNumber (String)를 가집니다. 생성자는 메시지와 전화번호를 매개변수로 받습니다. deliver() 메서드는 "Sending SMS to " + phoneNumber + ": " + message를 반환해야 합니다.
  • PushNotification.java: Notification을 상속받는 final 클래스를 생성합니다. PushNotification은 추가적인 프라이빗 필드 deviceId (String)를 가집니다. 생성자는 메시지와 기기 ID(device ID)를 매개변수로 받습니다. deliver() 메서드는 "Sending push to device " + deviceId + ": " + message를 반환해야 합니다.
  • Main.java: 알림 시스템을 하나로 합칩니다! 메시지(String), 이메일 주소(String), 전화번호(String), 기기 ID(String)의 네 가지 입력을 받게 됩니다.

    동일한 메시지를 사용하되 각각의 연락처 정보를 사용하여 각 알림 유형의 인스턴스를 하나씩 생성합니다. 봉인된 클래스의 다형성을 보여주기 위해 세 가지 알림을 모두 Notification[] 타입의 배열에 저장합니다. 그런 다음 배열을 순회하며 각 알림에 대해 deliver()를 호출한 결과를 출력합니다.

메시지, 이메일 주소, 전화번호, 기기 ID 순서대로 네 개의 입력을 받게 됩니다.

봉인된 클래스가 계층 구조를 어떻게 제한하는지 확인해 보세요. 허용된 세 개의 클래스만 Notification을 상속받을 수 있습니다. 각 서브클래스는 반드시 final, sealed, 또는 non-sealed로 선언되어야 합니다. 이를 통해 다형성을 활용하면서도 타입 계층 구조를 완벽하게 제어할 수 있습니다!

치트 시트

봉인된 클래스(sealed class)sealedpermits 키워드를 사용하여 어떤 클래스가 이를 상속할 수 있는지 제한합니다:

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

permits에 나열된 클래스들만 봉인된 클래스를 상속할 수 있습니다. 그 외의 클래스가 상속을 시도하면 컴파일 오류가 발생합니다.

허용된 각 서브클래스는 다음 세 가지 제어자 중 하나를 사용해야 합니다:

  • final - 더 이상의 상속을 방지합니다
  • sealed - 자체적인 허용 서브클래스 목록을 통해 제한 체인을 유지합니다
  • non-sealed - 제한 없는 상속이 가능하도록 계층 구조를 엽니다
sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // 상속될 수 없음
non-sealed class Truck extends Vehicle { }    // 누구나 Truck을 상속할 수 있음

봉인된 클래스는 결제 수단, 응답 유형 또는 기하학적 도형과 같이 도메인 내의 고정된 타입 집합을 모델링하는 데 유용합니다. 컴파일러가 가능한 모든 서브타입을 알고 있기 때문에 switch 표현식의 패턴 매칭과 함께 사용하기 좋습니다.

직접 해보기

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 네 개의 입력을 읽습니다
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: 동일한 message를 사용하여 각 알림 유형별로 하나씩 생성하세요
        // - message와 email을 사용하는 EmailNotification
        // - message와 phoneNumber를 사용하는 SMSNotification
        // - message와 deviceId를 사용하는 PushNotification
        
        // TODO: 세 가지 알림을 모두 Notification[] 배열에 저장하세요
        // 이것은 sealed 클래스를 사용한 다형성을 보여줍니다
        
        // TODO: 배열을 반복하며 각 알림에 대해 deliver()의 결과를 출력하세요
        // 각 알림에 대하여
        
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨