[2026] Kotlin 함수 | 함수 정의, 람다, 고차 함수
이 글의 핵심
Kotlin 함수: 함수 정의, 람다, 고차 함수. 함수 정의·람다식.
들어가며
함수는 일급 객체로 다루어져, 변수에 담고 고차 함수의 인자로 넘기기 쉽습니다. 람다·익명 함수 문법이 짧아, 컬렉션 연산과 잘 맞습니다.
실무에서 마주한 현실
개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.
1. 함수 정의
기본 함수
Kotlin 함수의 기본 형태입니다: 다음은 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)
}
함수 선언 위치: 다음은 kotlin를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 최상위 함수 (파일 레벨)
fun topLevel() {
println("최상위 함수")
}
class MyClass {
// 멤버 함수 (클래스 내부)
fun memberFunction() {
println("멤버 함수")
}
// 지역 함수 (함수 내부)
fun outer() {
fun inner() {
println("지역 함수")
}
inner() // 지역 함수 호출
}
}
기본 매개변수
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
fun greet(name: String = "Guest", greeting: String = "안녕하세요") {
println("$greeting, $name님!")
}
greet() // 안녕하세요, Guest님!
greet("홍길동") // 안녕하세요, 홍길동님!
greet("홍길동", "Hello") // Hello, 홍길동님!
greet(greeting = "Hi") // Hi, Guest님!
가변 인자
아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 실행 예제
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. 람다식
기본 문법
아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 람다 정의
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
체이닝 예제: 아래 코드는 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. 고차 함수
함수를 매개변수로
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
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
함수를 반환
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
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. 확장 함수
기본 확장
아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// String에 함수 추가
fun String.addExclamation() = "$this!"
println("Hello".addExclamation()) // Hello!
// Int에 함수 추가
fun Int.isEven() = this % 2 == 0
println(4.isEven()) // true
println(5.isEven()) // false
실전 확장 함수
아래 코드는 kotlin를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 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("test@example.com".isValidEmail()) // true
5. 스코프 함수
let
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
val name: String? = "홍길동"
name?.let {
println("이름: $it")
println("길이: ${it.length}")
}
apply
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// 실행 예제
data class Person(var name: String = "", var age: Int = 0)
val person = Person().apply {
name = "홍길동"
age = 25
}
also
다음은 간단한 kotlin 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
val numbers = mutableListOf(1, 2, 3)
.also { println("초기 리스트: $it") }
.also { it.add(4) }
.also { println("최종 리스트: $it") }
run
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// 실행 예제
val result = "Hello".run {
println(this)
length
}
println(result) // 5
with
아래 코드는 kotlin를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
val person = Person("홍길동", 25)
with(person) {
println(name)
println(age)
}
6. 인라인 함수
아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
inline fun measureTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val end = System.currentTimeMillis()
println("실행 시간: ${end - start}ms")
}
measureTime {
Thread.sleep(1000)
}
7. 실전 예제
예제 1: 함수형 프로그래밍
다음은 kotlin를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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 스타일
다음은 kotlin를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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