Kotlin in action 2/e: 람다를 사용한 프로그래밍(1) 코틀린의 람다

2026. 2. 14. 02:30·Kotlin

코드 블록을 값으로 다루기

프로그래밍을 하다보면, 코드 블록 자체를 값으로 다루면 편리한 경우가 있을 것이다. 이를테면 일련의 동작을 변수에 저장한다거나, 다른 함수에 넘겨줘야 하는 경우가 바로 그 예시이다. 책의 초반부에서 함수가 1급 시민이라고 언급되어 있듯이, 이는 코틀린에서 활발히 사용된다! 

 

뿐만 아니라 자바 8부터도 코드 블록을 값으로 다룰 수 있도록 람다식, 함수의 1급 시민화 등을 도입했다고 한다. 

 

 익명 함수 람다

{ x: Int, y: Int -> x + y }

 

보통 람다식은 위와 같이 작성된다. 나 또한 코틀린을 배우기 전에는 위와 같은 람다식밖에 몰랐다. 중괄호로 둘러쌓여 있고, -> 를 이용해 파라미터와 본문을 구분한다. 

fun main() {
    val sum = { x: Int, y: Int -> x + y }
    println(sum(1, 2))
}

 

이러한 람다식은 값으로 다룰 수 있기에 변수에 저장할 수 있으며, 호출 또한 일반적인 함수처럼 가능하다!

 

하지만 책에서는 람다를 만들자마자 바로 호출하는 것보다 람다 본문의 코드를 직접 실행시키는 편이 좀 더 낫다고 말한다. 

람다 본문에 있는 코드를 실행: run()

run() 함수는 인자로 받은 람다를 실행해 주는 라이브러리 함수이다. 특히 다음과 같이 어떤 변수를 정의하는 데에 몇 가지 설정과 다른 추가작업이 필요하다면 다음과 같이 사용해 볼 수도 있을 것이다. 

val myFavoriteNumber = run {
    println("I'm thinking")
    println("I'm doing some more work...")
}

 

컬렉션을 for 루프로 직접 검색하는 코드

data class Person(val name: String, val age: Int)

fun findTheOldest(people: List<Person>) {
    var maxAge = 0
    var theOldest: Person? = null
    for (person in people) {
        if (person.age > maxAge) {
            maxAge = person.age
            theOldest = person
        }
    }
    println(theOldest)
}

fun main() {
    val people = listOf(Person("Alice", 29), Person("Bob", 31))
    findTheOldest(people)
}

 

위의 예제는 Person 객체 중에서 가장 많은 나이를 가진 객체를 출력하는 코드이다. 코틀린에서는 표준 라이브러리 함수를 이용하면 이를 정말 간단하게 줄일 수 있다. 이 포스팅에서 정리할 내용을 많이 건너뛴 결과물이긴 하지만, 최종 축약된 결과는 다음과 같다. 

maxByOrNull 함수를 이용하여 간단하게 줄이기

fun main() {
    val people = listOf(Person("Alice", 29), Person("Bob", 31))
    println(people.maxByOrNull { it.age })
}

 

maxByOrNull 함수는 비교에 사용될 값을 인자로 받는다. 이 코드는 처음 보면 굉장히 생소하다, 특히 다음과 같은 점이 눈에 띈다.

 

it 사용: 이는 문맥 상 유추해볼 수 있긴 하다. Person 리스트에 대한 함수이므로, 각 Person 객체의 age를 접근하기 위해 사용했다고 볼 수 있을 것이다. it을 사용하지 않을 수도 있으나 그렇다면 it 대신 특정한 이름을 붙여줘야 할 것이다. 

 

중괄호 사용: 특이하게도, 소괄호에 인자를 던져주는 것이 아닌 중괄호에 던져주고 있다. 마치 코드블럭을 던져주는 것처럼 보이기도 한다. 어떻게 이러한 결과가 나오게 되었는지 정리해보겠다. 

 

정석적으로 람다 함수 전달하기

people.maxByOrNull({ p: Person -> p.age })

 

위 코드는 아까 코드보다는 좀 더 길지만 코틀린의 코드를 처음 보는 사람에 한하여 가독성은 조금 더 좋을 것 같다. 우선 소괄호를 사용하고 있기 때문에 중괄호로 감싸진 코드블럭을 매개변수에 전달하고 있음을 좀 더 잘 알 수 있다. 

 

또한 매개변수 Person 타입의 p를 명시적으로 정의하고 있으며, 본문과 매개변수를 -> 를 이용하여 구분하는 전형적인 람다식의 형태이다. 

 

위 코드는 정석적으로 문법의 의도를 잘 나타내고는 있지만, 약간 번잡함이 느껴진다. 

 

람다가 유일한 인자일 때

람다가 어떤 함수의 맨 뒤의 인자라면, 그 람다를 괄호 밖으로 빼낼 수 있다. 때문에 위의 코드는 다음과 같이 쓸 수 있다. 

people.maxByOrNull() { p: Person -> p.age }

 

람다가 어떤 함수의 유일한 인자이고, 위처럼 괄호 뒤에 람다를 썼다면 호출 시 빈 괄호를 없앨 수 있다.

people.maxByOrNull { p: Person -> p.age }

 

컴파일러가 람다 파라미터의 타입을 추론할 수 있을 때

코틀린 컴파일러는 람다 파라미터의 타입도 추론할 수 있다. 따라서 람다 파라미터의 타입을 추론할 수 있는 상황에서는 타입을 명시하지 않아도 된다. 

위의 경우에는 Person 타입의 객체 리스트에서 age라는 프로퍼티까지 호출하고 있으므로 p의 타입을 굳이 명시하지 않아도 Person임을 유추할 수 있다. 

people.maxByOrNull { p -> p.age }

 

람다의 파라미터가 하나뿐일때

람다의 파라미터가 하나뿐이고, 타입을 컴파일러가 추론할 수 있을 때, 자동으로 it이라는 파라미터를 만들어준다. 

people.maxByOrNull { it.age }

 

결론적으로 처음에 본 그 간단한 식이 만들어졌다,

 

 

주의할 부분: 람다를 변수에 저장할 때

람다를 변수에 저장할 때에는, 다음과 같이 저장하게 된다.

val getAge = { p: Person -> p.age }
people.maxByOrNull(getAge)

 

이 경우에는 단순히 동작을 저장하기 떄문에  파라미터의 타입을 컴파일러가 추론할 수 없으므로, 파라미터 타입을 명시해줘야 한다. 

 

본문이 여러 줄인 람다 식

마치 식 본문 함수에 return을 명시하지 않는 여러 줄의 식을 갖는 블록을 넣을 수 있듯이 람다 식에서도 여러 줄의 식을 갖는 블록을 갖도록 할 수 있다. 

 

fun main() {
    val sum = { x: Int, y: Int -> 
        println("Computing the sum of $x and $y...")
        x + y
    }
    println(sum(1, 2))
}

 

동일하게 맨 마지막 식이 람다의 결과값이 되며, 마찬가지로 return을 명시하지 않아도 된다. 

 

함수 파라미터를 람다 안에서 사용하기

코틀린과 자바 람다의 차이는 바로 람다 안에서 외부의 변수를 조작할 수 있다는 것이다. 코틀린 람다는 내부에서 외부의 파이널 변수가 아닌 변수에 접근, 변경할 수 있다.

 

설명이 자바의 문법을 기준으로 되어있어 헷갈릴 수 있으나 코틀린의 문법으로 바꾸면 다음과 같다. 

코틀린 람다는 내부에서 외부의 var로 선언된 변수에 접근, 변경할 수 있다. (val은 접근만 가능)

결국 람다 내부에서도 외부의 변수를 자유롭게 사용할 수 있다는 뜻이다. 

 

자바에서는 외부의 변수가 effectively final 이어야지만 자유롭게 사용할 수 있다는 제약이 있다. 사실 내가 코틀린을 먼저 시작하다보니, 람다 내에서 외부 변수를  너무 당연하게 사용해왔다는 생각이 든다

코틀린에서 이러한 편리함이 어떻게 구현되어있는지 추후 정리해보려고 한다.

 

 

람다를 비동기적으로 실행되는 코드로 활용하는 경우

람다를 이벤트 핸들러나 다른 비동기적으로 실행되는 코드로 활용하는 경우, 로컬 변수 변경은 람다가 실행될 때만 발생할 것이다.

 

다음 코드는 버튼 클릭 횟수를 제대로 셀 수 없다. 

fun tryToCountButtonClicks(button: Button): Int {
    var clicks = 0
    button.onClick { click++ }
    return clicks
}

 

위 함수는 항상 0을 반환한다. 하지만 이는 어찌보면 당연한 결과이다. clicks 변수의 생명 주기는 함수 tryToCountButtonClicks()까지 일뿐만 아니라, return을 통해 값을 얻어낸다. 

 

반면에 button.onClick { click++ } 부분은 지금 즉시 실행되는 코드가 아닌, 클릭 시 실행되는 코드이므로, 문제가 발생할 것이다. 

 

때문에 다음과 같은 흐름으로 동작하게 될 가능성이 크다

함수 호출 -> 클릭 리스너 등록 -> 함수 리턴 -> 사용자가 버튼 클릭 -> click++ 실행

 

해결 방안은 당연하게도, click 변수의 생명 주기를 함수 밖까지 연장하는 것이므로, click 변수를 함수 외부에 선언해야 할 것이다.

 

멤버 참조하기

사실 이미 함수로 선언된 코드를 값으로 넘겨주는 것은 방금까지 설명했듯 해당 함수를 호출하는 람다를 만들면 될 것이다. 하지만, 코틀린에서는 이 부분에 대해 편리한 방법을 제공한다. 바로 멤버 참조이다

val getAge = Person::age

 

위 코드는 Person 클래스의 age 프로퍼티에 접근하는 함수 값을 만들어줄 것이다. 즉 클래스 이름::참조하려는 멤버로 다음과 같은 식을 위처럼 좀 더 간결하게 나타낼 수 있다. 

val getAge = { person: Person -> person.age }

 

생성자 참조하기

:: 뒤에 클래스 이름을 넣으면 해당 클래스의 생성자를 참조할 수도 있다!

val getConstructor = ::Person

 

 

 

'Kotlin' 카테고리의 다른 글

Kotlin in action 2/e: 널이 될 수 있는 값(1): nullability, safe call(?.) elvis operator(?:), safe cast(as?)  (0) 2026.02.16
Kotlin in action 2/e: 람다를 사용한 프로그래밍(2) 함수형 인터페이스, 수신 객체 지정 람다  (0) 2026.02.15
Kotlin in action 2/e: 클래스, 객체, 인터페이스(2) 기본 구현 메서드, by, object  (0) 2026.02.13
Kotlin in action 2/e: 클래스, 객체, 인터페이스(1) 인터페이스, 클래스, 생성자  (0) 2026.02.09
Kotlin in action 2/e: 함수 정의와 호출(2) 로컬 함수, 코드를 확장 함수로 추출하기  (0) 2026.02.09
'Kotlin' 카테고리의 다른 글
  • Kotlin in action 2/e: 널이 될 수 있는 값(1): nullability, safe call(?.) elvis operator(?:), safe cast(as?)
  • Kotlin in action 2/e: 람다를 사용한 프로그래밍(2) 함수형 인터페이스, 수신 객체 지정 람다
  • Kotlin in action 2/e: 클래스, 객체, 인터페이스(2) 기본 구현 메서드, by, object
  • Kotlin in action 2/e: 클래스, 객체, 인터페이스(1) 인터페이스, 클래스, 생성자
Kirbyyy
Kirbyyy
개인적인 일상과 회고를 기록하는 블로그입니다.
  • Kirbyyy
    커브볼의 생존일지
    Kirbyyy
  • 전체
    오늘
    어제
    • 분류 전체보기 (53)
      • 우아한테크코스 (8)
      • 프로덕트 빌드 (0)
      • Problem Solving (20)
      • C++ (0)
      • Kotlin (19)
      • Java (3)
      • CS (2)
        • AI (2)
      • 취미생활 (0)
        • 서평 (0)
        • 프라모델 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    그리디 알고리즘
    백준 알고리즘
    백준 1356
    우테코 8기
    백준 RGB 거리
    백준 파도반 수열
    C++
    너비 우선 탐색
    Problem Solving
    ProblemSolving
    백준 11123
    백준 31575
    백준 연속 합
    백준 33272
    분할 정복
    백준 16174
    백준 16173
    BFS
    다이나믹 프로그래밍
    백준
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Kirbyyy
Kotlin in action 2/e: 람다를 사용한 프로그래밍(1) 코틀린의 람다
상단으로

티스토리툴바