더북(TheBook)

이 문제를 ‘유니버설 타입(universal type)’으로 해결할 수도 있을 것 같다. 유니버설 타입은 모든 타입의 부모 타입이다. 코틀린에서는 Any가 유니버설 타입이다. Any라는 이름의 뜻대로 Any 타입은 모든 타입의 인자를 허용한다. 어떤 함수에 여러 타입의 값을 넘겨야 하는데, 각 타입 사이에 공통점이 없다면 Any가 문제를 해결해준다.

언뜻 생각해보면 GenericHolder.kt에서 T 대신 Any를 써도 될 것 같다.

IntroGenerics/AnyInstead.kt

package introgenerics
import atomictest.eq

class AnyHolder(private val value: Any) {
  fun getValue(): Any = value
}

class Dog {
  fun bark() = "Ruff!"
}

fun main() {
  val holder = AnyHolder(Dog())
  val any = holder.getValue()  
  // 컴파일되지 않음
  // any.bark()
  
  val genericHolder = GenericHolder(Dog())
  val dog = genericHolder.getValue()
  dog.bark() eq "Ruff!"
}

간단한 경우에는 Any가 작동하지만, 구체적인 타입이 필요해지면(예를 들어 Dogbark()를 호출) 제대로 작동하지 않는다. 객체를 Any 타입으로 대입하면서 객체 타입이 Dog이라는 사실을 더 이상 추적할 수 없기 때문이다. DogAny로 전달하기 때문에 결과는 그냥 Any이고, Anybark()를 제공하지 않는다.

여기서 제네릭스를 사용하면 실제 컬렉션에 Dog을 담고 있다는 정보를 유지할 수 있다. 이 말은 getValue()가 돌려주는 값에 대해 bark()를 적용할 수 있다는 뜻이다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.