1. 태크 클래스(tagged class)

class Car(
    val type: CarType,        // enum 태그 (SPORTS, FAMILY)
    val name: String,
    val maxSpeed: Int?,       // Sports car만 필요
    val turboEngine: Boolean?, // Sports car만 필요
    val seatingCapacity: Int?, // Family car만 필요
    val trunkSize: Int?        // Family car만 필요
) {
    enum class CarType {
        SPORTS,
        FAMILY
    }

    fun describe() = when(type) {
        CarType.SPORTS -> "${name}은 최고속도 ${maxSpeed}km/h의 스포츠카입니다."
        CarType.FAMILY -> "${name}은 ${seatingCapacity}인승 패밀리카입니다."
    }
}

// 사용
val porsche = Car(
    type = Car.CarType.SPORTS,
    name = "911",
    maxSpeed = 300,
    turboEngine = true,
    seatingCapacity = null,  // 불필요
    trunkSize = null         // 불필요
)

2. 태크 클래스의 문제점

3. sealed 한정자

주요 특징

  1. 상속 제한
sealed class Car    // sealed로 선언

// 같은 파일 안에서만 Car를 상속할 수 있음
class SportsCar(val maxSpeed: Int) : Car() // OK
class FamilyCar(val seatingCapacity: Int) : Car() // OK

// 다른 파일(OtherCars.kt)에서는 상속 불가능
class ElectricCar(val batteryCapacity: Int) : Car() // 컴파일 에러!
  1. 하위 클래스를 컴파일 타임에 알 수 있음

    sealed class Car
    class SportsCar(val maxSpeed: Int) : Car()
    class FamilyCar(val seatingCapacity: Int) : Car()
    
    fun processCar(car: Car) = when(car) {
        is SportsCar -> "최고속도: ${car.maxSpeed}km/h"
        is FamilyCar -> "좌석수: ${car.seatingCapacity}인승"
        // 다른 타입이 없다는 것을 컴파일러가 알고 있어서
        // else나 다른 케이스가 필요 없음
    }
    
open class Car  // 일반 open 클래스
class SportsCar : Car()
class FamilyCar : Car()

fun processCar(car: Car) = when(car) {
    is SportsCar -> "스포츠카"
    is FamilyCar -> "패밀리카"
    else -> "다른 차량"  // else 필수! (다른 하위 클래스가 추가될 수 있음)
}