- 람다는 기본적으로 다른 함수에 넘길 수 있는 작은 코드 조각을 뜻한다.
- 이를 사용하면 쉽게 공통 코드 구조를 라이브러리 함수로 뽑아내는 것이 가능하다.
(1) 람다 소개: 코드 블럭을 함수 인자로 넘기기
button.setOnClickListener(new OnClickListener) {
@Override
public void onClick(View view) {
/* 버튼을 클릭 시 실행 할 동작 */
}
}
button.setOnClickListener { /* 버튼을 클릭 시 실행 할 동작*/ }
- 함수형 프로그래밍에서는 함수를 하나의 값 처럼 다루어서 번거로운 문제들을 해결한다.
- 클래스를 선언하고 인서턴스를 함수에 넘기는 대신, 함수를 직접 다른 함수에 전달한다.
- 람다 식을 사용하면, 함수를 선언할 필요가 없고, 코드 블록을 직접 함수의 인자로 전달하는 것이 가능하다.
- 해당 예제들은 람다를 하나뿐인 무명 객체 대신 사용할 수 있다는 사실을 보여준다.
(2) 람다와 컬렉션
data class Person(val name: String, val age: Int)
fun findTheOldest(people: List<Person>) {
var maxAge = 0 // 가장 많은 나이 저장
var theOldset: Person? = null // 가장 연장자인 사람 저장
for(person in people) {
if(person.age > maxAge) {
maxAge = person.age // 현재까지 발견한 최연장자보다 더 나이가 많으면 변경
theOldest = person
}
}
println(theOldest)
}
val people = listOf(Person("Alice", 29), Person("Bob", 31))
findTheOldest(people)
// Person(name=Bob, age=31)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.maxBy { it.age }) // 나이 프로퍼티를 비교하여 가장 값이 큰 원소 찾기
// Person(name=Bob, age=31)
people.maxBy(Person::age)
- 코드에서 중복을 제거하는 것은 스타일 개선을 위한 중요한 방법 중 하나이다.
- maxBy는 가장 큰 원소를 찾기 위해 비교에 사용할 값을 돌려주는 함수를 인자로 받는다.
- { it.age }는 바로 비교에 사용할 값을 돌려주는 함수이다.
(3) 람다 식의 문법
val people = listOf((Person("이몽룡", 29), Person("성춘향", 31))
val names = people.joinToString(" ") { p: Person -> p.name }
println(names) // 이몽룡 성춘향
- 람다는 값처럼 여기저기 전달이 가능한 동작의 모음이다. 이는 따로 선언해서 변수에 저장하는 것도 가능하다.
- 함수에 인자로 넘기면서 바로 람다를 정의하는 것이 대부분이다.
- 코틀린 람다 식은 항상 중괄호로 쌓여 있으며, 화살표가 인자 목록과 람다 본문을 구분해준다.
- 코드의 일부분을 블록으로 둘러싸서 실행할 필요가 있다면 run을 사용한다. 이는 인자로 받은 람다를 실행해준다.
(4) 현재 영역에 있는 변수에 접근
fun printProblemCounts(responses: Collection<String>) {
var clientErrors = 0 // 람다에서 사용할 변수 정의
var serverErrors = 0
responses.forEach {
if(it.startsWith("4")) clientErrors++
else if(it.startsWith("5")) serverErrors++
}
println("$clientErrors client errors, $serverErrors server errors")
}
val responses = listOf("200 OK", "418 I'm a teapot", "500 Internal Server Error")
printProblemCounts(responses) // 1 client errors, 1 server errors
fun tryToCountButtonClicks(button: Button): Int {
var clicks = 0
button.onClick { clicks++ }
return clicks
}
- forEach는 가장 기본적인 컬렉션 조작 함수 중 하나이다.
- 이는 컬렉션의 모든 원소에 대해서 람다를 호출해준다.
- Kotlin의 람다 안에서는 파이널 변수가 아닌 변수에 접근하는 것이 가능하다. 또한, 안에서 바깥의 변수를 변경해도 된다.
- 해당 예제의 prefix, clientErrors, serverErrors처럼 람다 안에서 사용하는 외부 변수를 람다가 포획한 변수라 한다.
- 어떤 함수가 자신의 로컬 변수를 포획한 람다를 반환하거나 다른 변수에 저장한다면 생명주기가 변경된다.
- 람다를 이벤트 핸들러나 다른 비동기적으로 실행되는 코드로 활용하는 경우, 함수 호출이 끝난 후에 변수가 변경될 수도 있다.
- 위 예제의 함수는 항상 0을 반환한다. onClick 핸들러가 호출될 때마다 clicks를 증가시키나, 변경을 관찰할 수 없다.
- 핸들러는 함수가 clicks를 반환한 후 호출되는 이유이다.
(5) 멤버 참조
val getAge = { person: Person -> person.age }
Person::age // 두 식은 같은 의미임
- 코틀린에서는 멤버 참조를 사용하여 함수를 값으로 바꿀 수 있다.
- 멤버 참조는 프로퍼티나 메서드를 단 하나만 호출하는 함수 값을 만들어준다.
- 멤버 참조는 그 멤버를 호출하는 람다와 같은 타입이며, 최상위에 선언된 함수나 프로퍼티를 참조할 수 있다.