Files
kotlin-examples/docs/08_특수 기능.md
2025-02-22 01:33:42 +09:00

6.2 KiB

코틀린의 특수 기능과 주요 키워드

코틀린은 개발 생산성을 높이는 다양한 특수 기능을 제공한다.
이 글에서는 코틀린의 고급 기능사용 예시와 함께 설명하겠다. 🚀

소개할 내용

  1. inline 함수
  2. reified 키워드
  3. 제너릭(Generic)
  4. 위임(Delegation) 패턴과 by 키워드

1. inline 함수: 함수 호출 비용 줄이기

코틀린의 inline 함수는 함수 호출 오버헤드를 줄이기 위해 사용된다.

1.1. 기본적인 inline 함수 예제

inline fun execute(block: () -> Unit) {
    println("실행 시작")
    block()
    println("실행 종료")
}

fun main() {
    execute {
        println("실제 로직 실행")
    }
}

출력 결과:

실행 시작
실제 로직 실행
실행 종료
  • 함수 호출이 사라지고 코드가 그대로 복사됨 (인라이닝)
  • 람다를 인자로 받을 때 유용
  • 단점: 코드 크기가 증가할 수 있음 (작은 함수에만 사용)

2. reified 키워드: 실행 시점에서 타입 유지

코틀린에서는 제너릭 타입 정보가 실행 시점에 사라지는 문제(type erasure)가 있음.
하지만 reified 키워드를 사용하면 실행 시점에서도 타입을 유지할 수 있음.

2.1. reified 없이 제너릭 타입 확인 불가

fun <T> getClassName(): String {
    return T::class.simpleName  // 오류 발생!
}

fun main() {
    println(getClassName<String>())  
}
  • 오류 발생! 제너릭 타입은 실행 시점에서 사라지기 때문.

2.2. reified 키워드 사용하여 해결

inline fun <reified T> getClassName(): String {
    return T::class.simpleName ?: "알 수 없음"
}

fun main() {
    println(getClassName<String>())  // "String"
    println(getClassName<Int>())     // "Int"
}
  • reified를 사용하면 실행 시점에서도 제너릭 타입을 알 수 있음
  • 주의: inline 함수에서만 reified 사용 가능

3. 제너릭(Generic): 타입을 유연하게 만들기

제너릭을 사용하면 코드의 재사용성을 높이고, 타입 안정성을 유지할 수 있음.

3.1. 기본적인 제너릭 사용

class Box<T>(val value: T) {
    fun get(): T = value
}

fun main() {
    val intBox = Box(123)
    val strBox = Box("Hello")

    println(intBox.get()) // 123
    println(strBox.get()) // Hello
}
  • T를 사용하여 어떤 타입이든 저장할 수 있는 클래스를 정의

3.2. 제너릭 함수 사용

fun <T> printItem(item: T) {
    println("아이템: $item")
}

fun main() {
    printItem(42)        // 아이템: 42
    printItem("코틀린")  // 아이템: 코틀린
}
  • 함수에도 제너릭 적용 가능

4. 위임(Delegation) 패턴과 by 키워드

코틀린의 by 키워드는 객체의 기능을 다른 객체에 위임할 때 사용된다.
이를 통해 불필요한 코드 중복을 방지할 수 있다.

4.1. 인터페이스를 이용한 기본 위임 패턴

interface Printer {
    fun printMessage()
}

class ConsolePrinter : Printer {
    override fun printMessage() {
        println("콘솔에 출력")
    }
}

class SmartPrinter(private val printer: Printer) : Printer {
    override fun printMessage() {
        println("스마트 프린터 작동 중...")
        printer.printMessage()
    }
}

fun main() {
    val printer = SmartPrinter(ConsolePrinter())
    printer.printMessage()
}

출력 결과:

스마트 프린터 작동 중...
콘솔에 출력
  • SmartPrinterConsolePrinter의 기능을 위임(Delegation)

4.2. by 키워드를 사용한 간결한 위임 패턴

class SmartPrinter2(private val printer: Printer) : Printer by printer

fun main() {
    val printer = SmartPrinter2(ConsolePrinter())
    printer.printMessage()  // "콘솔에 출력"
}
  • by printer 를 사용하면 위임을 자동으로 처리
  • 코드가 훨씬 간결해짐

5. by lazy를 활용한 지연 초기화

by lazy를 사용하면 객체를 필요할 때만 초기화할 수 있다.

class Example {
    val lazyValue: String by lazy {
        println("초기화 중...")
        "Hello, Kotlin!"
    }
}

fun main() {
    val example = Example()
    println("객체 생성 완료")
    println(example.lazyValue)  // 여기서 초기화 발생
    println(example.lazyValue)  // 이미 초기화됨, 출력만 함
}

출력 결과:

객체 생성 완료
초기화 중...
Hello, Kotlin!
Hello, Kotlin!
  • by lazy최초 접근 시점에만 실행됨
  • 이후에는 이미 생성된 값을 사용

6. object 키워드: 싱글톤 객체

코틀린에서 싱글톤 객체를 만들 때 object 키워드를 사용한다.

object Singleton {
    fun showMessage() {
        println("싱글톤 객체 실행!")
    }
}

fun main() {
    Singleton.showMessage()
}

출력 결과:

싱글톤 객체 실행!
  • 별도의 인스턴스 생성 없이 Singleton.showMessage() 사용 가능
  • 앱 설정, 네트워크 요청 관리 등에 유용

🔹 정리: 코틀린의 특수 기능 요약

기능 설명 사용 예시
inline 함수 함수 호출을 줄이고 성능 향상 inline fun execute(block: () -> Unit) {}
reified 실행 시점에서도 제너릭 타입 유지 inline fun <reified T> getType() = T::class.simpleName
제너릭 코드의 재사용성과 타입 안정성 유지 class Box<T>(val value: T)
Delegation 클래스 기능을 다른 객체에 위임 class PrinterImpl : Printer by ConsolePrinter()
by lazy 지연 초기화 val name: String by lazy { "코틀린" }
object 싱글톤 객체 object Singleton {}

코틀린의 강력한 기능들을 활용하면 코드를 더 간결하고 효율적으로 작성할 수 있다.
이제 직접 사용해보면서 익숙해지자! 🚀