220 lines
6.2 KiB
Markdown
220 lines
6.2 KiB
Markdown
# **코틀린의 특수 기능과 주요 키워드**
|
|
|
|
코틀린은 **개발 생산성을 높이는 다양한 특수 기능**을 제공한다.
|
|
이 글에서는 **코틀린의 고급 기능**을 **사용 예시와 함께** 설명하겠다. 🚀
|
|
|
|
### ✨ **소개할 내용**
|
|
1. **`inline` 함수**
|
|
2. **`reified` 키워드**
|
|
3. **제너릭(Generic)**
|
|
4. **위임(Delegation) 패턴과 `by` 키워드**
|
|
|
|
---
|
|
|
|
## **1. `inline` 함수: 함수 호출 비용 줄이기**
|
|
코틀린의 `inline` 함수는 **함수 호출 오버헤드를 줄이기 위해 사용**된다.
|
|
|
|
### **1.1. 기본적인 `inline` 함수 예제**
|
|
```kotlin
|
|
inline fun execute(block: () -> Unit) {
|
|
println("실행 시작")
|
|
block()
|
|
println("실행 종료")
|
|
}
|
|
|
|
fun main() {
|
|
execute {
|
|
println("실제 로직 실행")
|
|
}
|
|
}
|
|
```
|
|
**출력 결과:**
|
|
```
|
|
실행 시작
|
|
실제 로직 실행
|
|
실행 종료
|
|
```
|
|
> - **함수 호출이 사라지고 코드가 그대로 복사됨 (인라이닝)**
|
|
> - **람다를 인자로 받을 때 유용**
|
|
> - 단점: **코드 크기가 증가할 수 있음 (작은 함수에만 사용)**
|
|
|
|
---
|
|
|
|
## **2. `reified` 키워드: 실행 시점에서 타입 유지**
|
|
코틀린에서는 **제너릭 타입 정보가 실행 시점에 사라지는 문제(type erasure)가 있음**.
|
|
하지만 `reified` 키워드를 사용하면 **실행 시점에서도 타입을 유지할 수 있음**.
|
|
|
|
### **2.1. `reified` 없이 제너릭 타입 확인 불가**
|
|
```kotlin
|
|
fun <T> getClassName(): String {
|
|
return T::class.simpleName // 오류 발생!
|
|
}
|
|
|
|
fun main() {
|
|
println(getClassName<String>())
|
|
}
|
|
```
|
|
> - 오류 발생! **제너릭 타입은 실행 시점에서 사라지기 때문**.
|
|
|
|
### **2.2. `reified` 키워드 사용하여 해결**
|
|
```kotlin
|
|
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. 기본적인 제너릭 사용**
|
|
```kotlin
|
|
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. 제너릭 함수 사용**
|
|
```kotlin
|
|
fun <T> printItem(item: T) {
|
|
println("아이템: $item")
|
|
}
|
|
|
|
fun main() {
|
|
printItem(42) // 아이템: 42
|
|
printItem("코틀린") // 아이템: 코틀린
|
|
}
|
|
```
|
|
> - **함수에도 제너릭 적용 가능**
|
|
|
|
---
|
|
|
|
## **4. 위임(Delegation) 패턴과 `by` 키워드**
|
|
코틀린의 `by` 키워드는 **객체의 기능을 다른 객체에 위임할 때 사용**된다.
|
|
이를 통해 **불필요한 코드 중복을 방지**할 수 있다.
|
|
|
|
### **4.1. 인터페이스를 이용한 기본 위임 패턴**
|
|
```kotlin
|
|
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()
|
|
}
|
|
```
|
|
**출력 결과:**
|
|
```
|
|
스마트 프린터 작동 중...
|
|
콘솔에 출력
|
|
```
|
|
> - `SmartPrinter`가 `ConsolePrinter`의 기능을 **위임(Delegation)**
|
|
|
|
### **4.2. `by` 키워드를 사용한 간결한 위임 패턴**
|
|
```kotlin
|
|
class SmartPrinter2(private val printer: Printer) : Printer by printer
|
|
|
|
fun main() {
|
|
val printer = SmartPrinter2(ConsolePrinter())
|
|
printer.printMessage() // "콘솔에 출력"
|
|
}
|
|
```
|
|
> - `by printer` 를 사용하면 **위임을 자동으로 처리**
|
|
> - 코드가 훨씬 간결해짐
|
|
|
|
---
|
|
|
|
## **5. `by lazy`를 활용한 지연 초기화**
|
|
`by lazy`를 사용하면 **객체를 필요할 때만 초기화**할 수 있다.
|
|
```kotlin
|
|
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` 키워드**를 사용한다.
|
|
```kotlin
|
|
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 {}` |
|
|
|
|
코틀린의 강력한 기능들을 활용하면 **코드를 더 간결하고 효율적으로 작성**할 수 있다.
|
|
이제 **직접 사용해보면서** 익숙해지자! 🚀 |