Kotlin : sealed class vs abstract class

Murjune·2024년 6월 16일
3

kotlin/Java

목록 보기
7/8
post-thumbnail

안드로이드 개발자라면 아마 다음과 코드를 통해 sealed class 에 대해 처음 접했을 것이다.

sealed class UiState {
    class Success<out T>(data: T) : UiState()
    class Error(e: Throwable?) : UiState()
    data object Loading : UiState()
}

봉인된 클래스..? when 절에서 else를 안써도 된다고?
enum class 랑 비슷하네 🤨
잘 모르겠지만 일단 좋은거 같다 그냥 쓰자

대부분의 사람들이 위와 같이 생각했을 것이다.

사실 sealed class 는 abstract class 다! 좀 더 자세하게 말하면, sealed class 는 sub class를 모두 알고 있는 Special 한 abstract class 이다.

이번 포스팅에서는 sealed class 와 abstract class 를 비교해보면서 차이점과 장점에 대해 정리해보고자 한다.

abstract class vs sealed class

공식문서

sealed class 가 abstract class 와 다른 점은 sub class 들을 반드시 같은 패키지에 넣어줘야 한다는 것이다.

컴파일러에게 서브클래스들을 찾는 비용을 줄이기 위해서다.
이러한 제약조건이 없다면 모든 모듈/패키지에 있는 파일에서 sub class가 있는지 Compiler 가 일일이 찾아야 한다. 이러한 오버 헤드를 방지하기 위해 서브 클래스는 반드시 같은 패키지 안에 있어야하는 제약 조건을 거는 것이다.

컴파일러는 sealed class의 sub class 들을 모두 알고 있습니다. 따라서,when 절을 사용할 때 else 절을 추가적으로 작성하지 않아도 된다.

sealed class UiState {
    class Success<out T>(data: T) : UiState()
    class Error(e: Throwable?) : UiState()
    data object Loading : UiState()
}

fun foo(uiState: UiState) {
    return when (uiState) {
        is UiState.Success<*> -> println("Success")
        is UiState.Error -> println("Error")
        UiState.Loading -> println("Loading")
        // else 문을 사용하지 않아도 된다. 😎
    }
}

Sealed class 의 장점

else 를 생략할 수 있기에 얻는 장점이 무엇일까? 🤔

sealed class의 sub class 를 추가했을 때 when 절에서 컴파일 에러를 발생시켜 추가적인 로직 처리를 강제한다는 것이다.

만약, sealed class 가 아닌 abstract class 로 UiState 를 구현했다고 해보자.

abstract class UiState {
    class Success<out T>(data: T) : UiState()
    class Error(e: Throwable?) : UiState()
    data object Loading : UiState()
}

abstract class 의 경우 when 절을 사용할 때 반드시 else 문을 작성해줘야한다. (컴파일러가 sub class들을 모르기 때문)

fun foo(uiState: UiState) = when (uiState) {
        is UiState.Success<*> -> println("Success")
        is UiState.Error -> println("Error")
        UiState.Loading -> println("Loading")
        else -> println("else") 
    }
}

이때, UiState 에 Skeletone 이라는 서브 클래스를 추가한다고 해보자

data object Skeletone: UiState()

Skeletone 이 새롭게 추가 되어도 else 문 때문에, UiState 를 사용하고 있는 곳에서는 아무 에러가 없다.

아무 에러가 없다니.. 좋은거 아닌가??
개발자에게 가장 좋은 Error는 Compile Error 다. Runtime Error 는 Build 할 때 발생하는 에러이기에, 만약 개발자가 이를 처리하지 않고 배포한다면, 사용자가 앱을 사용하다가 어플이 비정상종료될 수 있다.😵

런타임 에러를 방지하기 위해 개발자는 UiState 를 사용하고 있는 클라이언트 코드들을 모두 일일이 찾아 분기처리문을 추가해줘야 한다..🤯 만약, 실수로 한 곳이라도 분기 처리문 처리를 놓친다면 런타임 에러 혹은 논리 에러가 발생할 것이다.

그럼 이번에는 else 문을 지우고 abstract 키워드를 다시 sealed 키워드로 바꿔주자! 다음과 같이 컴파일 에러가 뜬다.

컴파일러가 발생하기에 개발자는 이를 쉽게 인지할 수 있다.
이제 컴파일 에러가 발생하는 곳에 분기처리문을 추가해주면 된다. 😎

컴파일 에러를 통해 논리 에러를 방지할 수 있다는 것이 sealed class 의 가장 큰 장점이다!!

따라서, else 문을 사용한다면 sealed class 를 사용할 이유가 없다고 생각한다.

abstract class 와 sealed class를 사용하는 기준

오케이 sealed class 의 장점에 대해 잘 알았다.
그럼, 이제 무조건 abstract class 가 아닌 sealed class 를 사용하면 되는 걸까?

당연히 아니다. 항상 sealed class를 사용해야 한다면 abstract class 가 삭제됐겠죠? ㅋㅅㅋ

어떤 class 의 구현체를 외부(다른 모듈)에서 구현하도록 설계할 경우가 있다. 이러한 경우, abstract class 를 사용하는 것이 좋다.

보통 Library 가 위의 경우에 해당한다.

Android Library 에서 제공하는 RecyclerView Adapter 는 abstract class 이다. 만약, RecyclerView Adapter 가 sealed class 였다면, 우리는 Adapter를 상속받아 CustomAdapter를 구현하여 사용할 수 없을 것이다..

sealed class의 경우 외부에서 sub class를 만드는 행위가 모두 제한되기 때문이다. 서브 클래스는 반드시 sealed class 가 위치하는 패키지에 존재해야 한다.

// 리사이클러뷰 어뎁터
public abstract static class Adapter<VH extends ViewHolder> {
// 커스텀 어뎁터, 만약 sealed class 라면 상속받아 구현할 수 없다.
class CustomAdapter: RecyclerView.Adapter

정리)
abstract class: 다른 모듈/패키지에서 서브 클래스를 구현해야할 경우
sealed class: 해당 class의 서브 클래스를 해당 패키지에서만 구현할 경우

부록) 컴파일러는 어떻게 sealed class의 Sub class 를 찾을 수 있는거지?

sealed class UiState {
    class Success<out T>(data: T) : UiState()
    class Error(e: Throwable?) : UiState()
    data object Loading : UiState()
    data object Skeleton : UiState()
}

UiState 를 자바로 디컴파일 해보자!

  • 1) Sealed class 는 디컴파일 시 abstract class로 변환된다.
  • 2) @Metadata 어노테이션을 통해 해당 class 의 패키지 경로와, sub class들에 대한 정보를 알려준다.
  • 3) synthetic method : 컴파일러만 해당 생성자에 접근 가능(다른 클래스에서 생성자 열 수 없음)

@Metadata 어노테이션에 있는 정보를 기반으로 Compiler 는 서브 클래스들을 찾는다 ㅎ ㅎ


더 궁금하신 분은 다음 포스팅을 봐주세요 😎

sealed class vs enum class

Reference

https://medium.com/hongbeomi-dev/sealed-class%EC%99%80-sealed-interface-db1fff634860

https://kotlinlang.org/docs/sealed-classes.html#declare-a-sealed-class-or-interface

profile
열심히 하겠슴니다:D

1개의 댓글

comment-user-thumbnail
2024년 9월 18일

안녕하세요 모르던 지식을 배우고 갑니다.
잘 배우고 갑니다. ㅎㅎ

답글 달기

관련 채용 정보