본문 바로가기
개발&프로그래밍

[kotlin] Sealed classes & interfaces

by 재아군 2024. 8. 14.

Kotlin의 Sealed 클래스와 인터페이스

Kotlin의 sealed 클래스와 인터페이스는 클래스 계층 구조의 제어된 상속을 제공하는 강력한 기능입니다.

이 가이드에서는 sealed 클래스와 인터페이스의 다양한 측면을 상세히 살펴보겠습니다.

 

 

sealed 클래스와 인터페이스 기본 (Sealed Classes and Interfaces Basics)

sealed 클래스와 인터페이스는 제한된 상속을 구현할 때 사용됩니다. sealed 클래스의 모든 직접 하위 클래스는 컴파일 시점에 알려져 있어야 하며, 같은 모듈과 패키지 내에서만 정의될 수 있습니다.

sealed interface Error

sealed class IOError(): Error

class FileReadError(val file: File): IOError()
class DatabaseError(val source: DataSource): IOError()

object RuntimeError : Error

 

이 예제는 라이브러리의 API에서 오류 클래스 계층을 나타낼 수 있습니다. sealed를 사용함으로써 라이브러리 작성자는 모든 가능한 오류 유형을 알 수 있고, 외부에서 새로운 오류 유형이 추가되는 것을 방지할 수 있습니다.

 

 

생성자 (Constructors)

sealed 클래스는 추상 클래스이므로 직접 인스턴스화할 수 없지만, 생성자를 포함하거나 상속할 수 있습니다:

sealed class Error(val message: String) {
    class NetworkError : Error("Network failure")
    class DatabaseError : Error("Database cannot be reached")
    class UnknownError : Error("An unknown error has occurred")
}

fun main() {
    val errors = listOf(Error.NetworkError(), Error.DatabaseError(), Error.UnknownError())
    errors.forEach { println(it.message) }
}

 

 

sealed 클래스의 생성자는 protected(기본값) 또는 private 가시성을 가질 수 있습니다:

sealed class IOError {
    constructor() { /*...*/ }
    private constructor(description: String): this() { /*...*/ }
}

 

 

상속 (Inheritance)

sealed 클래스와 인터페이스의 직접 하위 클래스는 같은 패키지 내에 선언되어야 합니다. 이들은 최상위 레벨이거나 다른 명명된 클래스, 인터페이스, 객체 내에 중첩될 수 있습니다.

sealed interface Error

enum class ErrorType : Error {
    FILE_ERROR, DATABASE_ERROR
}

sealed class IOError(): Error

open class CustomError(): Error

 

 

when 표현식과 함께 사용 (Use with when Expression)

sealed 클래스의 주요 이점은 when 표현식과 함께 사용할 때 나타납니다:

fun log(e: Error) = when(e) {
    is Error.FileReadError -> println("Error while reading file ${e.file}")
    is Error.DatabaseError -> println("Error while reading from database ${e.source}")
    Error.RuntimeError -> println("Runtime error")
    // else 절이 필요 없음
}

컴파일러가 모든 경우를 검사할 수 있어 else 절이 필요 없습니다.

 

 

실제 사용 사례 (Use Case Scenarios)

UI 상태 관리 (UI State Management)

sealed class UIState {
    data object Loading : UIState()
    data class Success(val data: String) : UIState()
    data class Error(val exception: Exception) : UIState()
}

fun updateUI(state: UIState) {
    when (state) {
        is UIState.Loading -> showLoadingIndicator()
        is UIState.Success -> showData(state.data)
        is UIState.Error -> showError(state.exception)
    }
}

 

 

결제 방법 처리 (Payment Method Handling)

sealed class Payment {
    data class CreditCard(val number: String, val expiryDate: String) : Payment()
    data class PayPal(val email: String) : Payment()
    data object Cash : Payment()
}

fun processPayment(payment: Payment) {
    when (payment) {
        is Payment.CreditCard -> processCreditCardPayment(payment.number, payment.expiryDate)
        is Payment.PayPal -> processPayPalPayment(payment.email)
        is Payment.Cash -> processCashPayment()
    }
}

 

 

API 요청-응답 처리 (API Request-Response Handling)

sealed interface ApiRequest

@Serializable
@Resource("login")
data class LoginRequest(val username: String, val password: String) : ApiRequest

@Serializable
@Resource("logout")
object LogoutRequest : ApiRequest

sealed class ApiResponse {
    data class UserSuccess(val user: UserData) : ApiResponse()
    data object UserNotFound : ApiResponse()
    data class Error(val message: String) : ApiResponse()
}

fun handleRequest(request: ApiRequest): ApiResponse {
    return when (request) {
        is LoginRequest -> {
            if (isValidUser(request.username, request.password)) {
                ApiResponse.UserSuccess(UserData("userId", "userName", "userEmail"))
            } else {
                ApiResponse.Error("Invalid username or password")
            }
        }
        is LogoutRequest -> {
            ApiResponse.UserSuccess(UserData("userId", "userName", "userEmail"))
        }
    }
}

 

 

Kotlin의 sealed 클래스와 인터페이스는 타입 안전성과 코드의 표현력을 높이는 강력한 도구입니다. 이들은 특히 상태 관리, 오류 처리, API 설계 등에서 매우 유용하게 사용됩니다.

 

sealed 클래스와 인터페이스를 사용하면 컴파일러가 모든 가능한 하위 클래스를 알 수 있어, when 표현식에서 모든 경우를 처리했는지 확인할 수 있습니다. 이는 런타임 오류를 줄이고 코드의 안정성을 높이는 데 큰 도움이 됩니다.

또한, sealed 클래스와 인터페이스는 코드의 구조를 더 명확하게 만들어 주며, 특히 복잡한 도메인 모델이나 상태 머신을 설계할 때 매우 유용합니다.

 

하지만 sealed 클래스와 인터페이스는 같은 모듈과 패키지 내에서만 하위 클래스를 정의할 수 있다는 제한이 있으므로, 이 점을 고려하여 적절히 사용해야 합니다.

 

 


Kotlin, SealedClass, SealedInterface, 패턴매칭, 상태관리, 오류처리, API설계, 타입안전성, 코틀린문법, 안드로이드개발, 서버개발, 함수형프로그래밍, when표현식, 객체지향프로그래밍, 코드구조화

댓글