Kotlin Multiple Smart Casting Issue

발단

오늘 회사 동기가 다음과 같은 코드가 제대로 작동하지 않는다는 이야기를 해주었다.

예시 코드는 다음과 같다.

class Cat (
    val age:Long,
    val name: String
)
class Dog (
    val age:Long,
    val name:String
)

여기 개와 고양이라는 두개의 클래스가 있다.

두 클래스는 모두 같은 Property를 갖고 있다.

프로퍼티 이름도, 타입도 같은 것을 확인할 수 있다.

fun<T> get(animal:T) {
    when(animal) {
        is Cat -> println("Age is ${animal.age}, Name is ${animal.name}")
        is Dog -> println("Age is ${animal.age}, Name is ${animal.name}")
    }
}

fun main() {
    val cat = Cat(1, "cat")
    val dog = Dog(1, "dog")
    get(cat)
    get(dog)
}

메인 코드는 간단하다.

함수는 제네릭 타입이며 내부에서는 스마트 캐스팅을 사용해서 해당 객체의 프로퍼티 값을 출력하는 아주 간단한 코드이다.

당연히 위 코드의 출력은 다음과 같다.

Age is 1, Name is cat
Age is 1, Name is dog

여기서 동기의 생각은 다음과 같다. (나의 동기는 클린코드를 추구하는 멋진 개발자임을 밝힌다.)

같은 프로퍼티를 갖고 있다면 굳이 나눌 필요가 있을까? 중복 코드를 없애자!

그래서 다음과 같은 리팩토링을 시도하였다.

fun<T> get(animal:T) {
    when(animal) {
        is Cat, is Dog -> println("Age is ${animal.age}, Name is ${animal.name}")
    }
}

fun main() {
    val cat = Cat(1, "cat")
    val dog = Dog(1, "dog")
    get(cat)
    get(dog)
}

위 코드를 실행하면 컴파일 타임에 에러를 발생시켰고 동기는 나에게 물었다.

이거 왜 안되죠 해리?

해답

왜 위 코드는 안될까?

Cat과 Dog의 프로퍼티는 타입도 같고 심지어 이름도 같다.

그러나 여기서 우리는 아주 중요한 사실을 하나 간과하고 있었다.

그것은 바로 코틀린은 컴파일 타임에 타입이 결정되는 언어라는 점이다.

animal은 제네릭 타입이다.

when절에서 제네릭 타입을 명시적 타입으로 스마트 캐스팅 하고 있는 것을 확인할 수 있다.

이 스마트 캐스팅이 2개 이상이 될 때 문제가 발생한다.

앞서 코틀린이 컴파일 언어라서 컴파일 시점에 타입이 결정된다고 이야기 했는데, 스마트 캐스팅을 2개를 컨디션으로 걸게 되면 코틀린 컴파일러는 어떤 타입인지를 알 수 없기 때문에 에러를 발생시키게 되는 것이다.

런타임에 타입이 결정되는 동적 인터프리터 언어와 달리, 정적 컴파일 언어인 코틀린에서 스마트 캐스팅을 사용할 때는 반드시 해당 제네릭 타입이 어떤 타입인지를 명시해주어야만 그 타입의 프로퍼티나 메소드를 사용할 수 있다.

이 점을 놓쳤기 때문에 위와 같은 문제가 발생했던 것이다.

한줄 요약

코틀린에서 when 절을 사용할 때 Mutiple Smart Casting Condition은 불가능하다!

Reference

My Head



© 2022. by minkuk

Powered by minkuk