Kotlin 고급 문법
Advanced Kotlin Syntax
Kotlin 고급 문법
Kotlin의 기본 문법에 이어, 실무와 코딩 테스트에서 자주 활용되는 고급 문법을 정리한다.
1. Lambda
1
2
3
4
5
val square: (Int) -> Int = { number -> number * number }
fun main() {
println(square(5)) // 25
}
람다식은 value처럼 다룰 수 있는 익명 함수이다. 변수에 저장하거나 함수의 인자로 전달할 수 있다.
축약 표현
파라미터가 하나인 경우 it 키워드로 축약할 수 있다.
1
2
3
val double: (Int) -> Int = { it * 2 }
val names = listOf("Alice", "Bob", "Charlie")
val lengths = names.map { it.length } // [5, 3, 7]
여러 줄 람다
마지막 표현식이 반환값이 된다.
1
2
3
4
5
val processName: (String) -> String = { name ->
val trimmed = name.trim()
val upper = trimmed.uppercase()
upper // 이 값이 반환됨
}
2. Higher-Order Functions (고차 함수)
함수를 매개변수로 받거나 함수를 반환하는 함수이다.
1
2
3
4
5
6
7
8
9
10
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
fun main() {
val sum = operate(3, 4) { x, y -> x + y }
val product = operate(3, 4) { x, y -> x * y }
println(sum) // 7
println(product) // 12
}
마지막 파라미터가 람다인 경우, 괄호 밖으로 뺄 수 있다 (trailing lambda).
1
2
3
// 아래 두 표현은 동일
listOf(1, 2, 3).filter({ it > 1 })
listOf(1, 2, 3).filter { it > 1 }
3. Extension Functions (확장 함수)
기존 클래스를 수정하지 않고 새로운 함수를 추가할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
fun String.addExclamation(): String {
return this + "!"
}
fun Int.isEven(): Boolean = this % 2 == 0
fun main() {
println("Hello".addExclamation()) // Hello!
println(4.isEven()) // true
println(7.isEven()) // false
}
실용적인 예시
1
2
3
4
5
6
7
8
9
10
fun <T> List<T>.secondOrNull(): T? {
return if (this.size >= 2) this[1] else null
}
fun String.toSlug(): String {
return this.lowercase()
.replace(Regex("[^a-z0-9\\s-]"), "")
.replace(Regex("\\s+"), "-")
.trim('-')
}
4. Scope Functions (범위 함수)
Kotlin은 객체의 컨텍스트 내에서 코드 블록을 실행하는 5개의 scope function을 제공한다.
| 함수 | 객체 참조 | 반환값 | 사용 사례 |
|---|---|---|---|
let |
it |
람다 결과 | null 체크 후 실행 |
run |
this |
람다 결과 | 객체 설정 + 결과 계산 |
with |
this |
람다 결과 | 객체의 여러 메서드 호출 |
apply |
this |
객체 자체 | 객체 초기화/설정 |
also |
it |
객체 자체 | 부수 효과 (로깅 등) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
data class Person(
var name: String = "",
var age: Int = 0,
var email: String = ""
)
// let: null-safe 처리
val name: String? = "Kotlin"
name?.let {
println("Name length: ${it.length}")
}
// apply: 객체 초기화
val person = Person().apply {
this.name = "DooDoo"
this.age = 25
this.email = "doodoo@example.com"
}
// also: 디버깅/로깅
val numbers = mutableListOf(1, 2, 3)
.also { println("Before: $it") }
.apply { add(4) }
.also { println("After: $it") }
// run: 객체 설정 + 결과
val greeting = person.run {
"Hello, $name! You are $age years old."
}
// with: 여러 속성 접근
val info = with(person) {
println(name)
println(age)
"$name ($age)"
}
5. Data Class & Destructuring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data class User(val name: String, val age: Int, val email: String)
fun main() {
val user = User("DooDoo", 25, "doodoo@example.com")
// 자동 생성: toString, equals, hashCode, copy
println(user) // User(name=DooDoo, age=25, email=doodoo@example.com)
val older = user.copy(age = 26) // name, email은 유지
// Destructuring
val (name, age, email) = user
println("$name is $age years old") // DooDoo is 25 years old
// Map에서도 활용
val map = mapOf("a" to 1, "b" to 2)
for ((key, value) in map) {
println("$key -> $value")
}
}
6. Sealed Class
상속 가능한 클래스들의 집합을 제한할 때 사용한다. when 표현식에서 else 분기가 필요 없어진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
object Loading : Result<Nothing>()
}
fun handleResult(result: Result<String>) {
when (result) {
is Result.Success -> println("Data: ${result.data}")
is Result.Error -> println("Error: ${result.message}")
is Result.Loading -> println("Loading...")
// else 불필요 - 컴파일러가 모든 케이스를 확인
}
}
7. Collection Operations
Kotlin의 컬렉션 API는 함수형 프로그래밍 스타일의 다양한 연산을 제공한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// 기본 변환
val doubled = numbers.map { it * 2 } // [2, 4, 6, ..., 20]
val evens = numbers.filter { it % 2 == 0 } // [2, 4, 6, 8, 10]
val sum = numbers.reduce { acc, n -> acc + n } // 55
// 그룹화 & 분할
val grouped = numbers.groupBy { if (it % 2 == 0) "even" else "odd" }
// {odd=[1, 3, 5, 7, 9], even=[2, 4, 6, 8, 10]}
val (small, large) = numbers.partition { it <= 5 }
// small=[1,2,3,4,5], large=[6,7,8,9,10]
// flatMap
val nested = listOf(listOf(1, 2), listOf(3, 4), listOf(5))
val flat = nested.flatMap { it } // [1, 2, 3, 4, 5]
// 체이닝
val result = numbers
.filter { it % 2 == 0 }
.map { it * it }
.sortedDescending()
.take(3)
// [100, 64, 36]
8. Coroutines 기초
Kotlin 코루틴은 비동기 프로그래밍을 순차적인 코드처럼 작성할 수 있게 해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import kotlinx.coroutines.*
fun main() = runBlocking {
// launch: 결과를 반환하지 않는 코루틴
val job = launch {
delay(1000L)
println("World!")
}
println("Hello,")
job.join()
// async: 결과를 반환하는 코루틴
val deferred1 = async { fetchUserName() }
val deferred2 = async { fetchUserAge() }
println("${deferred1.await()} is ${deferred2.await()} years old")
}
suspend fun fetchUserName(): String {
delay(1000L) // 네트워크 호출 시뮬레이션
return "DooDoo"
}
suspend fun fetchUserAge(): Int {
delay(1000L)
return 25
}
주요 개념
suspend: 일시 중단 가능한 함수를 표시하는 키워드launch: 결과를 반환하지 않는 코루틴 빌더 (Job반환)async: 결과를 반환하는 코루틴 빌더 (Deferred<T>반환)runBlocking: 코루틴이 완료될 때까지 현재 스레드를 차단
1
2
3
4
5
6
7
// 구조화된 동시성 (Structured Concurrency)
suspend fun loadData() = coroutineScope {
val users = async { fetchUsers() }
val posts = async { fetchPosts() }
// 둘 다 완료될 때까지 대기
processData(users.await(), posts.await())
}
Dispatchers
1
2
3
4
// Dispatchers 종류
launch(Dispatchers.Main) { /* UI 작업 */ }
launch(Dispatchers.IO) { /* 네트워크, DB I/O */ }
launch(Dispatchers.Default) { /* CPU 집약 작업 */ }
Exception Handling
1
2
3
4
5
6
val handler = CoroutineExceptionHandler { _, exception ->
println("예외 처리: $exception")
}
val job = CoroutineScope(Dispatchers.IO + handler).launch {
throw RuntimeException("에러 발생")
}
Flow 기초
1
2
3
4
5
6
7
8
9
fun numberFlow(): Flow<Int> = flow {
for (i in 1..5) {
delay(100)
emit(i)
}
}
// collect
numberFlow().collect { value -> println(value) }
9. Inline Functions & Reified Types
inline 키워드는 함수 호출 오버헤드를 줄이고, reified는 제네릭 타입 정보를 런타임에 유지한다.
1
2
3
4
5
6
7
8
9
inline fun <reified T> List<*>.filterByType(): List<T> {
return this.filterIsInstance<T>()
}
fun main() {
val mixed: List<Any> = listOf(1, "hello", 2.0, "world", 3)
val strings = mixed.filterByType<String>() // [hello, world]
val ints = mixed.filterByType<Int>() // [1, 3]
}
References
댓글을 불러오는 중...