학습
Kotlin 제네릭과 타입 시스템
Kotlin 제네릭과 타입 시스템 — 중급
제네릭과 타입 시스템
1. 도입 (왜 배워야 하는가)
Kotlin의 제네릭(Generic)과 타입 시스템은 프로그래밍에서 데이터의 유형을 안전하게 다룰 수 있게 해주는 강력한 도구입니다. 실무에서는 다양한 데이터 형식을 처리할 때, 코드의 재사용성과 타입 안정성을 높이기 위해 제네릭을 사용합니다. 예를 들어, 쇼핑몰의 장바구니 시스템을 구현할 때 다양한 상품을 담을 수 있도록 하려면 제네릭이 필수적입니다.
제네릭을 모르면 타입 안전성을 보장받지 못해 런타임 오류가 발생할 수 있으며, 이는 프로그램의 신뢰성을 떨어뜨립니다. 예를 들어, 정수형 리스트에 문자열을 추가하려고 시도하는 경우 프로그램은 예기치 않게 종료될 수 있습니다. 따라서 제네릭은 안전하고 유연한 코드 작성을 위해 반드시 이해해야 할 개념입니다.
2. 핵심 개념 설명
제네릭은 클래스, 인터페이스 및 함수에서 사용할 수 있는 타입 매개변수를 정의하는 방법입니다. 이를 통해 특정 데이터 타입에 종속되지 않고 다양한 타입에 대해 일반화된 코드를 작성할 수 있습니다.
- 제네릭(Generic): 다양한 데이터 타입을 처리할 수 있도록 하는 프로그래밍 기법입니다. 마치 여러 브랜드의 물건을 담을 수 있는 다용도 가방과 같습니다.
- 타입(Type): 변수나 값의 데이터 형식을 뜻합니다. 예를 들어, 숫자, 문자열 등이 있습니다.
- 타입 매개변수(Type Parameter): 클래스나 함수에서 제네릭을 사용할 때 지정하는 매개변수입니다.
| 용어 | 설명 |
|---|---|
| 제네릭 | 다양한 타입을 처리할 수 있는 프로그래밍 기법 |
| 타입 매개변수 | 제네릭에서 사용하는 변수 형태의 타입 |
3. 기본 코드 예제
아래는 제네릭을 사용하는 간단한 리스트 클래스를 만들어보는 예제입니다.
class Box<T>(val item: T) {
fun getItem(): T {
return item
}
}
fun main() {
val intBox = Box(123) // Int 타입의 Box 생성
val stringBox = Box("Hello") // String 타입의 Box 생성
println(intBox.getItem()) // 123 출력
println(stringBox.getItem()) // Hello 출력
}
class Box<T>(val item: T): 제네릭 클래스 Box를 정의하며, T는 타입 매개변수입니다.fun getItem(): T: getItem 메서드는 T 타입의 아이템을 반환합니다.val intBox = Box(123): 정수 타입 Box를 생성합니다.println(intBox.getItem()): 123을 출력합니다.val stringBox = Box("Hello"): 문자열 타입 Box를 생성합니다.println(stringBox.getItem()): "Hello"를 출력합니다.4. 실전 활용 예제
- 장바구니 구현:
- 데이터베이스 레포지토리:
- 타입 불일치:
- 타입 매개변수의 사용을 잊음:
- 제네릭은 다양한 데이터 타입을 처리할 수 있도록 해주는 유용한 프로그래밍 기법이다.
- 타입 안전성을 보장함으로써 런타임 오류를 방지할 수 있다.
- 제네릭을 통해 코드의 재사용성을 높이고, 유지보수가 용이한 구조를 만들 수 있다.
- 다음에 학습하면 좋은 주제는 "고급 제네릭 사용법"이나 "타입 별칭(Type Alias)"입니다.
쇼핑몰에서 다양한 상품을 담을 수 있는 장바구니 클래스를 제네릭으로 구현할 수 있습니다.
class Cart<T> {
private val items = mutableListOf<T>()
fun addItem(item: T) {
items.add(item)
}
fun getItems(): List<T> {
return items
}
}
fun main() {
val cart = Cart<String>() // 문자열 타입의 장바구니 생성
cart.addItem("사과")
cart.addItem("바나나")
println(cart.getItems()) // [사과, 바나나] 출력
}
다양한 타입의 데이터를 저장하고 검색할 수 있는 데이터베이스 레포지토리를 구현할 수 있습니다.
class Repository<T> {
private val data = mutableListOf<T>()
fun save(item: T) {
data.add(item)
}
fun findAll(): List<T> {
return data
}
}
fun main() {
val intRepo = Repository<Int>()
intRepo.save(1)
intRepo.save(2)
println(intRepo.findAll()) // [1, 2] 출력
}
5. 흔한 실수와 주의사항
val box = Box("Hello") // String 타입
// box.item = 123 // 오류 발생: Int를 String으로 할 수 없음
- 올바른 코드:
val box: Box<Any> = Box("Hello") // Any로 선언하면 모든 타입 가능
class Box { // 잘못된 코드
fun getItem(): T { ... } // T가 정의되지 않음
}
- 올바른 코드:
class Box<T> { // T로 제네릭 클래스 정의
fun getItem(): T { ... }
}