
지난 Generic (1), (2) 포스팅에 이어서
Generic (3) 포스팅을 끝으로 제네릭에 대해 전부 알아보겠습니다
제네릭 타입 삭제 (Type Erasure)
kotlin은 런타임에 제네릭 타입 정보를 유지하지 않습니다.
→ 컴파일 시점에만 타입 정보가 사용되고 런타임에는 제네릭 타입에 대한 정보가 사라집니다.
is 연산자로 타입 체크를 할 수 없습니다.
fun <T> checkType(value: T) {
if (value is String) { // 오류: 'Cannot check for instance of erased type: T'
println("This is a String!")
} else {
println("This is not a String!")
}
}
fun main() {
checkType("Hello")
checkType(42)
}
대신 reified 타입 파라미터를 사용하는 인라인 함수로 이를 처리할 수 있습니다.
단, reified 타입은 인라인 함수에서만 사용할 수 있습니다.
inline fun <reified T> checkType(value: Any) {
if (value is T) {
println("This is a ${T::class.simpleName}!")
} else {
println("This is not a ${T::class.simpleName}!")
}
}
fun main() {
checkType<String>("Hello") // 출력: This is a String!
checkType<Int>("Hello") // 출력: This is not a Int!
}
제네릭 확장 함수
제네릭 확장 함수는 타입에 구애 받지 않고, 확장 가능한 함수를 정의할 수 있습니다.
fun <T : Number> T.square(): T {
return (this.toDouble() * this.toDouble()) as T
}
println(3.square()) // 출력: 9
println(2.5.square()) // 출력: 6.25
fun <T, R> T.convert(converter: (T) -> R): R {
return converter(this)
}
// 사용 예
val length = "Hello".convert { it.length }
println(length) // 출력: 5
스타 프로젝션 (*)
제네릭 타입의 구체적인 타입을 알지 못해도, *를 사용해 모든 타입을 허용할 수 있습니다.
- 구체적인 타입 인자를 알 수 없을 때 사용
- 모든 가능한 타입을 수용하고 싶을 때 사용
fun printMap(map: Map<*, *>) {
for ((key, value) in map) {
println("Key: $key, Value: $value")
}
}
val map = mapOf(1 to "One", 2 to "Two")
printMap(map)
// 출력:
// Key: 1, Value: One
// Key: 2, Value: Two
읽기 전용 (out)
스타 프로젝션에서 요소를 읽을 경우, 요소의 타입은 Any? 로 취급 됩니다.
이는 모든 타입의 상위 타입이므로 안전하게 쓸 수 있습니다.
fun readList(list: List<*>) {
for (item in list) {
println(item) // item의 타입은 Any?
}
}
readList(listOf(1, 2, 3)) // 출력: 1, 2, 3
readList(listOf("A", "B", "C")) // 출력: A, B, C
쓰기 전용 (in)
스타 프로젝션을 사용하는 경우, 제네릭 타입에 요소를 추가하는 작업은 허용되지 않습니다.
타입 안정성을 보장하기 위함입니다.
fun writeToList(list: MutableList<*>) {
list.add(1) // 오류
}

? extends
가끔 Java에서 제네릭 코드를 보면 <? extends> 이렇게 쓰인 곳이 있습니다.
하지만 Kotlin에서는 이러한 기능은 없으며,
이는 out 키워드가 동일한 역할을 수행합니다.
import java.util.List;
public class Main {
public static void printNumbers(List<? extends Number> list) {
for (Number number : list) {
System.out.println(number);
}
}
public static void main(String[] args) {
List<Integer> integers = List.of(1, 2, 3);
List<Double> doubles = List.of(1.1, 2.2, 3.3);
printNumbers(integers); // 출력: 1, 2, 3
printNumbers(doubles); // 출력: 1.1, 2.2, 3.3
}
}
fun printNumbers(list: List<out Number>) {
for (number in list) {
println(number)
}
}
fun main() {
val integers: List<Int> = listOf(1, 2, 3)
val doubles: List<Double> = listOf(1.1, 2.2, 3.3)
printNumbers(integers) // 출력: 1, 2, 3
printNumbers(doubles) // 출력: 1.1, 2.2, 3.3
}
'Kotlin' 카테고리의 다른 글
[Kotlin] Contract (0) | 2025.04.03 |
---|---|
[Kotlin] Builder Pattern 대체하기 (0) | 2025.01.13 |
[Kotlin] Generic (2) - 변성 (0) | 2024.12.10 |
[Kotlin] Generic (1) - 제네릭? (0) | 2024.12.10 |
[Kotlin] 위임 (Delegation) (2) | 2024.11.26 |