본문 바로가기
개발새발/코틀린

[코틀린/Kotlin] 구체화(reifeid)

by 조희우 2025. 9. 2.

reifeid

  • 일반적으로 제네릭 타입은 런타임 시점에서 사라짐(타입 소거, type erasure)
    fun <T> printType(value: T) {
        println(T::class) // ❌ 오류!
    }
    
    → T는 런타임에 어떤 타입인지 알 수 없어서 컴파일 에러
    → inline과 reified를 함께 사용하여 T의 실제 타입 정보를 런타임에서도 사용 가능:

⭑Type Erasure글 추가 예정

사용법

inline fun <reified T> printType(value: T) {
    println(T::class)
}
  • inline: 함수를 호출한 곳에 코드가 인라인으로 삽입(호출 지점에 복사) → 타입 정보 유지 가능
  • reified: 제네릭 타입 T를 런타임에서도 사용 가능

사용 이유

  • T::class, T::class.java가 필요한 경우
T::class / T::class.java

- T::class
   = Kotlin의 클래스 참조
   = 반환 타입: KClass<T>

- T::class.java
    = Java의 클래스 참조
    = 반환 타입: Class<T>

⭑ T::class, T::class.java글 추가 예정
  • value is T, value as? T처럼 런타임 타입 체크가 필요한 경우
  • Retrofit, Gson, Moshi등의 제네릭 기반 객체 변환, 타입 검사 유틸 함수 등에 자주 사용

예시

inline fun <reified T> isOfType(value: Any): Boolean {
	return value is T
}

fum main() {
	println(isOfType<String>("hello")) // true
	println(isOfType<Int>("hello")) //false
}

reified가 없는 경우 비교

reified 없이 구현한 경우

fun <T: Any> fromJson(json: String, clazz: Class<T>): T{
	return Gson().fromJson(json, clazz)
}
val json = """{"name":"희우","age":24}"""
val user = fromJson(json, User::class.java)
  • T의 실제 타입을 런타임 과정에서 알 수 없음
    → User::class.java를 반드시 인자로 넘겨야함
  • T: Any
    • non-null 타입을 명시
    • Java 라이브러리와의 연동 시 타입 안정성을 지킴

Class<T>대신 KClass<T>를 사용한 경우

  • Java에서 타입을 넘길 경우 String.class, User.class처럼 Class<T>를 사용
  • Kotlin에서는 ::class를 사용하여 KClass<T>를 사용하고 .java를 붙이면 Java의 Class<T>로 변환 가능
KClass<T>

- Kotlin에서는 클래스 타입 정보를 ::class로 가져옴: KClass<T>
- Java의 Class<T>와 대응되는 방식
- 리프랙션을 할 때 자주 사용하며 Java interop을 위하여 .java로 변환하기도함

[예시]
User::class = KClass<User>

⭑ KClass글 추가 예정
import com.google.gson.Gson
import kotlin.reflect.KClass

fun <T : Any> fromJsonWithKClass(json: String, kClass: KClass<T>): T {
    return Gson().fromJson(json, kClass.java) // Class<T>: kClass.java -> kClass
}
val json = """{"name": "희우", "age": 25}"""
val user = fromJsonWithKClass(json, User::class) // Class<T>: User::class -> User::class.java

reified를 사용한 경우

inline fun <reified T> fromJson(json: String): T{
	return Gson().fromJson(json, T::class.java)
}
val json = """{"name":"희우","age":24}"""
val user = fromJson(json)
  • T::class.java를 내부에서 직접 사용 가능

중첩 제네릭

  • List<User>와 같이 복잡한 타입은 T::class.java로는 부족하여 TypeToken을 사용해야함
  • reified로 완벽하게 해결하기 어려워 TypeToken을 따로 사용해야 할 수 있음

⭑ TypeToken 글 추가 예정

문제

1. reified를 활용한 타입 검사 함수 만들기

 

[요구사항]

checkType이라는 제네릭 확장 함수를 작성해보자.

fun <T> Any.checkType(): Boolean {
    // 구현할 내용
}
  • 호출한 객체가 제네릭 타입 T에 해당하는지 검사
  • reified 키워드를 사용해서 구현
  • is T를 활용

[사용 예시]

val value: Any = "Hello"

println(value.checkType<String>()) // true
println(value.checkType<Int>()) // false

 

 

답:

inline fun <reified T> Any.checkType(): Boolean {
	return this is T
}

 

 

2. reified와 Class<T> 비교하여 클래스 이름 출력하기

inline fun <reified T> printClassName()
  • T::class 와 T::class.java를 각각 출력하는 함수 구현

답:

inline fun <T: Any> printClassName(clazz: Class<T>) {
	println("Kotlin class: ${clazz.kotlin}") // Kotlin class: class java.lang.String (Kotlin reflection is not available)
	println("Java class: $clazz") //Java class: class java.lang.String
}

fun main(){
	printClassName(String::class.java)
}
inline fun <reified T> printClassName() {
	println("Kotlin class: ${T::class}") // Kotlin class: class java.lang.String (Kotlin reflection is not available)
	println("Java class: ${T::class.java}") // Java class: class java.lang.String
}

fun main(){
	printClassName<String>()
}