2024-12-19 @이영훈
들어가며
제네릭 타입을 다루다 보면 Kotlin과 Java에서 비슷하지만 다른 문법을 마주하게 됩니다.
대표적으로 Kotlin의 Any와 *, 그리고 Java의 ?이 있습니다.
저는 Any?, *의 차이를 이해하는 것이 중요했습니다.
1. Kotlin Any
•
의미: 모든 non-null 타입의 최상위 타입 (Java의 Object와 비슷)
•
Any?를 사용하면 nullable 타입까지 포함할 수 있습니다.
•
Collection에서 꺼냈을 때는 Any 또는 Any? 타입입니다.
•
예시:
val myList1: List<Any> = listOf("a", 1, true) // ⭕️
myList1[0] // 타입: Any
val myList2: List<Any> = listOf("a", 1, true, null) // ❌ 컴파일 에러. null을 입력할 수 없음
val myList3: List<Any?> = listOf("a", "b", null) // ⭕️ Any?이기 때문에 null을 입력할 수 있음
myList[0] // 타입: Any?
Kotlin
복사
2. Kotlin * (Star Projection)
•
의미: 타입 인자를 명시하지 않고, 정확한 타입을 모른다는 표시 (Java의 ?와 비슷)
•
null을 포함한 모든 타입을 포함합니다
•
Collection에서 꺼냈을 때는 Any? 타입입니다.
•
예시:
val myList: List<*> = listOf("a", 1, true, null) // ⭕️ null 타입을 입력할 수 있음
myList[0] // 타입: Any?
Kotlin
복사
중요한 차이점 ️
다형성 관점에서 List<*>는 "타입을 알 수 없으나, 최소한 읽을 수 있는(Read-only) 리스트"로 간주할 수 있습니다.
•
MutableList<Any>는 원소를 추가할 수 있지만 MutableList<*>은 원소를 추가할 수 없습니다.
•
예시:
val myList2: MutableList<Any> = mutableListOf("a", 1, 2L)
myList2.add(true) // ⭕️
val myList3: MutableList<*> = mutableListOf("a", 1, 2L)
myList3.add(2) // ❌ 컴파일 에러
Kotlin
복사
3. Java ? (Wildcard)
•
의미: 타입 매개변수를 알 수 없거나 특정 상하한 경계를 가진 제네릭
•
Collection에서 꺼냈을 때 Object 타입
•
예시:
void printAll(List<?> list) {
// 어떤 타입의 리스트인지 정확히 모름
for (Object elem : list) {
System.out.println(elem);
}
}
Java
복사
•
? extends T나 ? super T를 통해 상하한 경계를 지정할 수 있습니다.
•
Kotlin에서는 이를 out(covariance)와 in(contravariance) 키워드로 대체합니다.
⠀