본문 바로가기
🔓 영구 노트

sealed 클래스를 사용하여 else 분기 처리 없애기

by 파랭이가 룰루랄라 2022. 10. 10.

코틀린에서 when을 사용할 때 else 분기 처리를 하지 않고, 새로운 클래스 추가에도 유연하게 대처할 수 있는 방법을 설명한다.

- kotlin in action 4장 클래스, 객체, 인터페이스 p.156~158

- #살아있다 #자프링외길12년차 #코프링2개월생존기

상위 클래스에 sealed 변경자를 붙이면 그 상위 클래스를 상속한 하위 클래스 정의를 제한할 수 있다.
- kotlin in action p.157

하위 클래스를 제한하기 위해서 하위 클래스는 반드시 sealed 변경자가 붙은 상위 클래스 안에 중첩되어야 한다.

 

다음은 sealed 변경자가 붙은 상위 클래스와 하위 클래스 NewNote, NewPencil 코드이다.

sealed class Item<out T> {
    val type: String
        get() = javaClass.simpleName
    abstract val content: T

    data class NewNote(override val content: Note) : Item<Note>()
    data class NewPencil(override val content: Pencil) : Item<Pencil>()
}

class Note(
        val id: Int,
        val name: String
)

class Pencil(
        val id: Int,
        val name: String
)

NewNote와 NewPencil은 Item을 상속받고, 생성 시점에 content를 입력받는다. 여기서 Item은 open 변경자를 작성하지 않았지만 어떻게 상속이 가능하냐고 생각할 수 있다. 답은 코틀린에서 sealed 변경자가 붙은 클래스는 자동으로 open 변경자가 붙은 것으로 인식하기 때문이다.

else 분기 처리

fun mapItem(item: Item<*>) = when(item) {
    is Item.NewNote -> println("id = ${item.content.id}, name = ${item.content.name}, type = ${item.type}")
    is Item.NewPencil -> println("id = ${item.content.id}, name = ${item.content.name}, type = ${item.type}")
}

mapItem 함수는 item의 클래스에 따라 item의 id, name, type을 출력해준다. 그리고 sealed 클래스를 사용하니 else 분기 처리를 하지 않아도 컴파일 에러가 발생하지 않는다.

새로운 클래스 NewPencilCase

서비스에 추가적인 요구사항이 발생하여 Item에 NewPencilCase라는 하위 클래스가 추가되어야 한다고 가정하겠다. 요구사항에 맞게 클래스를 추가하면 어떻게 될까? 결과는 다음과 같다.

새로운 하위 클래스 추가 컴파일 에러

이처럼 sealed 클래스를 사용하여 when을 처리하면 새로운 클래스 처리를 잊어버려도 컴파일 에러를 발생시켜 개발자의 실수를 방지해준다. 결과적으로 새로운 클래스에 유연하게 대처할 수 있게 된 것이다.

테스트

fun main() {
    val pencil = Pencil(3, "나는 연필")
    val note = Note(4, "나는 공책")

    val newNote = Item.NewNote(note)
    val newPencil = Item.NewPencil(pencil)

    mapItem(newNote)
    printLine()

    mapItem(newPencil)
}

note와 pencil의 정보와 type이 제대로 출력되는 것을 확인할 수 있다.

댓글