Kotlin 함수 | 함수 정의, 람다, 고차 함수
이 글의 핵심
Kotlin 함수에 대한 실전 가이드입니다. 함수 정의, 람다, 고차 함수 등을 예제와 함께 상세히 설명합니다.
들어가며
함수는 일급 객체로 다루어져, 변수에 담고 고차 함수의 인자로 넘기기 쉽습니다. 람다·익명 함수 문법이 짧아, 컬렉션 연산과 잘 맞습니다.
1. 함수 정의
기본 함수
Kotlin 함수의 기본 형태입니다:
// 1. 반환값 없는 함수
fun greet(name: String) {
// fun: 함수 선언 키워드
// greet: 함수 이름
// (name: String): 매개변수 (이름: 타입)
// 반환 타입 생략 시 Unit (void와 유사)
println("안녕하세요, $name님!")
// $name: 문자열 템플릿 (변수 삽입)
}
greet("홍길동") // 안녕하세요, 홍길동님!
// 2. 반환값 있는 함수 (블록 본문)
fun add(a: Int, b: Int): Int {
// : Int : 반환 타입 명시 (필수)
return a + b
// return 키워드로 값 반환
}
val result = add(10, 20)
println(result) // 30
// 3. 표현식 함수 (단일 표현식)
fun add(a: Int, b: Int) = a + b
// = 뒤에 표현식 작성
// 반환 타입 자동 추론 (Int)
// return 키워드 불필요
// 간결하고 읽기 쉬움
// 표현식 함수 예시
fun max(a: Int, b: Int) = if (a > b) a else b
fun square(x: Int) = x * x
fun isEven(x: Int) = x % 2 == 0
println(max(10, 20)) // 20
println(square(5)) // 25
println(isEven(4)) // true
// 4. Unit 반환 (명시적)
fun printSum(a: Int, b: Int): Unit {
// Unit: 반환값이 없음을 명시 (생략 가능)
// Java의 void와 유사하지만 실제로는 싱글톤 객체
println(a + b)
// return 생략 가능 (자동으로 Unit 반환)
}
// Unit 생략 (권장)
fun printSum(a: Int, b: Int) {
println(a + b)
}
함수 선언 위치:
// 최상위 함수 (파일 레벨)
fun topLevel() {
println("최상위 함수")
}
class MyClass {
// 멤버 함수 (클래스 내부)
fun memberFunction() {
println("멤버 함수")
}
// 지역 함수 (함수 내부)
fun outer() {
fun inner() {
println("지역 함수")
}
inner() // 지역 함수 호출
}
}
기본 매개변수
fun greet(name: String = "Guest", greeting: String = "안녕하세요") {
println("$greeting, $name님!")
}
greet() // 안녕하세요, Guest님!
greet("홍길동") // 안녕하세요, 홍길동님!
greet("홍길동", "Hello") // Hello, 홍길동님!
greet(greeting = "Hi") // Hi, Guest님!
가변 인자
fun sum(vararg numbers: Int): Int {
return numbers.sum()
}
println(sum(1, 2, 3)) // 6
println(sum(1, 2, 3, 4, 5)) // 15
// 배열 전달
val nums = intArrayOf(1, 2, 3)
println(sum(*nums)) // spread 연산자
2. 람다식
기본 문법
// 람다 정의
val sum = { a: Int, b: Int -> a + b }
println(sum(10, 20)) // 30
// 타입 추론
val multiply: (Int, Int) -> Int = { a, b -> a * b }
// 단일 매개변수 (it)
val square: (Int) -> Int = { it * it }
println(square(5)) // 25
람다 활용
Kotlin의 컬렉션 함수와 람다를 함께 사용하는 예제입니다:
val numbers = listOf(1, 2, 3, 4, 5)
// 1. filter: 조건에 맞는 요소만 선택
val evens = numbers.filter { it % 2 == 0 }
// { it % 2 == 0 }: 람다 표현식
// it: 암묵적 매개변수 (단일 매개변수일 때 사용)
// it % 2 == 0: 짝수 판별 조건
//
// 동작:
// 1 → false (제외)
// 2 → true (포함)
// 3 → false (제외)
// 4 → true (포함)
// 5 → false (제외)
println(evens) // [2, 4]
// 명시적 매개변수 사용
val evens2 = numbers.filter { num -> num % 2 == 0 }
// num: 명시적 매개변수 이름
// it 대신 의미 있는 이름 사용 가능
// 2. map: 각 요소를 변환
val doubled = numbers.map { it * 2 }
// 각 요소에 2를 곱함
// [1, 2, 3, 4, 5] → [2, 4, 6, 8, 10]
println(doubled) // [2, 4, 6, 8, 10]
// map 실전 예시: 객체 변환
data class User(val name: String, val age: Int)
val users = listOf(
User("홍길동", 25),
User("김철수", 30)
)
val names = users.map { it.name }
println(names) // [홍길동, 김철수]
// 3. forEach: 각 요소에 대해 작업 수행 (반환값 없음)
numbers.forEach { println(it) }
// 출력:
// 1
// 2
// 3
// 4
// 5
// forEach는 반환값이 없으므로 체이닝 불가
// map/filter와 달리 부수 효과(side effect)를 위해 사용
// 4. reduce: 배열을 하나의 값으로 축약
val sum = numbers.reduce { acc, num -> acc + num }
// acc: 누적값 (accumulator)
// num: 현재 요소
//
// 동작:
// acc=1 (첫 요소), num=2 → 1+2=3
// acc=3, num=3 → 3+3=6
// acc=6, num=4 → 6+4=10
// acc=10, num=5 → 10+5=15
println(sum) // 15
// fold: 초기값 지정 가능
val sum2 = numbers.fold(0) { acc, num -> acc + num }
// 0: 초기값
// reduce는 첫 요소가 초기값, fold는 명시적 초기값
// 실전 예시: 문자열 연결
val words = listOf("Hello", "World", "Kotlin")
val sentence = words.reduce { acc, word -> "$acc $word" }
println(sentence) // Hello World Kotlin
체이닝 예제:
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val result = numbers
.filter { it % 2 == 0 } // 짝수만: [2, 4, 6, 8, 10]
.map { it * it } // 제곱: [4, 16, 36, 64, 100]
.filter { it > 20 } // 20 초과: [36, 64, 100]
.sum() // 합계: 200
println(result) // 200
3. 고차 함수
함수를 매개변수로
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
val result1 = calculate(10, 5) { a, b -> a + b } // 15
val result2 = calculate(10, 5) { a, b -> a * b } // 50
함수를 반환
fun makeMultiplier(factor: Int): (Int) -> Int {
return { number -> number * factor }
}
val double = makeMultiplier(2)
val triple = makeMultiplier(3)
println(double(5)) // 10
println(triple(5)) // 15
4. 확장 함수
기본 확장
// String에 함수 추가
fun String.addExclamation() = "$this!"
println("Hello".addExclamation()) // Hello!
// Int에 함수 추가
fun Int.isEven() = this % 2 == 0
println(4.isEven()) // true
println(5.isEven()) // false
실전 확장 함수
// List 확장
fun <T> List<T>.secondOrNull(): T? {
return if (size >= 2) this[1] else null
}
val list = listOf(1, 2, 3)
println(list.secondOrNull()) // 2
// String 확장
fun String.isValidEmail(): Boolean {
return matches(Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"))
}
println("[email protected]".isValidEmail()) // true
5. 스코프 함수
let
val name: String? = "홍길동"
name?.let {
println("이름: $it")
println("길이: ${it.length}")
}
apply
data class Person(var name: String = "", var age: Int = 0)
val person = Person().apply {
name = "홍길동"
age = 25
}
also
val numbers = mutableListOf(1, 2, 3)
.also { println("초기 리스트: $it") }
.also { it.add(4) }
.also { println("최종 리스트: $it") }
run
val result = "Hello".run {
println(this)
length
}
println(result) // 5
with
val person = Person("홍길동", 25)
with(person) {
println(name)
println(age)
}
6. 인라인 함수
inline fun measureTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val end = System.currentTimeMillis()
println("실행 시간: ${end - start}ms")
}
measureTime {
Thread.sleep(1000)
}
7. 실전 예제
예제 1: 함수형 프로그래밍
data class User(val name: String, val age: Int, val city: String)
fun main() {
val users = listOf(
User("홍길동", 25, "서울"),
User("김철수", 30, "부산"),
User("이영희", 28, "서울"),
User("박민수", 35, "서울")
)
// 서울에 사는 30세 미만 사용자
val result = users
.filter { it.city == "서울" }
.filter { it.age < 30 }
.map { it.name }
println(result) // [홍길동, 이영희]
}
예제 2: DSL 스타일
class HTML {
private val content = StringBuilder()
fun head(block: () -> Unit) {
content.append("<head>")
block()
content.append("</head>")
}
fun body(block: () -> Unit) {
content.append("<body>")
block()
content.append("</body>")
}
override fun toString() = content.toString()
}
fun html(block: HTML.() -> Unit): HTML {
return HTML().apply(block)
}
val page = html {
head { }
body { }
}
정리
핵심 요약
- 함수:
fun, 표현식 함수, 기본 매개변수 - 람다:
{ a, b -> a + b },it - 고차 함수: 함수를 매개변수/반환
- 확장 함수: 기존 클래스에 함수 추가
- 스코프 함수: let, apply, also, run, with
다음 단계
- Kotlin 클래스
- Kotlin 컬렉션
- Kotlin 코루틴
관련 글
- JavaScript 함수 | 함수 선언, 화살표 함수, 콜백, 클로저 완벽 정리
- Python 함수 | 매개변수, 반환값, 람다, 데코레이터 완벽 정리
- Swift 함수와 클로저 | 함수 정의, 클로저, 고차 함수
- C++ std::function vs 함수 포인터 |
- C++ 기본 인자 |