코틀린 람다 맛보기
컬렉션에서 직접 탐색하기
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)
}
>> val people = listOf(Person("Corn", 31), Person("HB", 27))
>> findTheOldest(people)
Person(name=Corn, age=31)
경험이 많은 개발자라면 순식간에 이런 루프를 작성할 수 있으나 이 루프에는 상당히 많은 코드가 들어있기 때문에 작성하다 실수를 저지르기 쉽다. 코틀린에서는 더 좋은 방법이 있다. 라이브러리 함수를 쓰면 된다.
// 모든 컬랙션에 대해 maxBy 함수를 호출할 수 있다.
println(people.maxBy{ it.age })
{ it.age } 는 바로 비교에 사용할 값을 돌려주는 함수다. 이 코드는 컬렉션 원소를 인자로 받아서(it이 그 인자)비교에 사용할 값을 반환한다.
이런식으로 단지 함수나 프로퍼티를 반환하는 역할을 수행하는 람다는 멤버 참조로 대치할 수 있다.
people.maxBy(Person::age)
람다식의 문법
람다는 값처럼 여기저기 전달할 수 있는 동작의 모음이다.
람다를 따로 선언해서 변수에 저장할수도 있지만 함수에 인자로 넘기면서 바로 람다를 정의하는 경우가 대부분이다. 아래는 람다 식을 선언하기 위한 문법이다.
코틀린 람다 식은 항상 중괄호로 둘러싸여 있고, 화살표(→)가 인자 목록과 람다 본문을 구분해준다.
>>> val sum = {x: Int, y: Int -> x+y}
>>> println(sum(1, 2))
3
// 원한다면 람다 식을 직접 호출해도 된다.
>>> { println(42) }()
// run은 인자로 받은 람다를 실행해 준다.
>>> run { println(42) }
람다식 문법 정리 아래의 람다식은 모두 같은 역할을 수행한다.
// 선언
val sum = { x: Int, y: Int -> x + y }
// 람다식 호출
val peopole = listOf(Person("Alice", 29), Person("Bob", 31))
people.maxBy({ p: Person -> p.age })
// 함수 호출 시 맨 뒤에 있는 인자가 람다 식이라면 그 람다를 괄호 밖으로 빼낼 수 있는 문법 관습이 있음
people.maxBy() { p: Person -> p.age }
// 람다가 어떤 함수의 유일한 인자이고 괄호 뒤에 람다를 썼다면 호출 시 빈 괄호를 없애도 됨
people.maxBy { p:Person -> p.age }
// 람다 파라미터가 하나이고, 타입을 추론할 수 있는 경우
people.maxBy{ it.age }
람다식 내에서의 변수 접근
람다를 함수 안에서 정의하면 함수의 파라미터뿐 아니라 람다 정의의 앞에 선언된 로컬 변수까지 람다에서 모두 사용할 수 있다.
- 자바
- 람다식 내에서 외부의 파이널 변수에서만 접근 가능
- 코틀린
- 람다식 내에서 외부의 로컬 변수에 접근 && 변경 가능
예제 코드를 살펴보자. 다음 리스트는 전달받은 상태 코드 목록에 있는 클라이언트와 서버 오류의 횟수를 세는 코드이다.
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 erros, $serverErrors server errors")
}
fun main() {
val responses = listOf("200 OK", "418 I'm a teapot", "500 Internal Server Error")
printProblemCounts(responses)
}
이 처럼 람다 안에서 사용하는 외부 변수를 '람다가 포획(capture)한 변수'라고 부른다.
포획한 변수가 있는 람다를 저장해서 함수가 끝난 뒤에 실행해도 람다의 본문 코드는 여전히 포획한 변수를 읽거나 쓸 수 있다. (로컬 변수의 생명주기가 함수가 반환되면 끝나는게 아니다. 로컬 변수를 포획한 람다를 반환한다면..)
멤버 참조
람다로 넘기려는 코드가 이미 함수로 선언된 경우는 어떻게 할까? 코틀린에서는 자바 8과 마찬가지로 함수를 값으로 바꿀 수 있다. 이때 이중 콜론(::)을 사용한다.
val getAge = Person::age
::를 사용하는 식을 멤버 참조(member reference)라고 부른다.
people.maxBy(Person::age)
people.maxBy{ p -> p.age }
people.maxBy{ it.age }
최상위에 선언된 함수나 프로퍼티를 참조할 수도 있다.
fun salute() = println("Salute!")
>>> run(::salute)
Salute!
생성자 참조(constructor reference)를 사용하면 클래스 생성 작업을 연기하거나 저장해둘 수 있다. :: 뒤에 클래스 이름을 넣으면 생성자 참조를 만들 수 있다.
data class Person(val name: String, val age: Int)
// Person 의 인스턴스를 만드는 동작을 값으로 저장한다.
>>> val createPerson = ::Person
>>> val p = createPerson("Alice", 29)
>>> println(p)
Person(name=Alice, age=29)
확장 함수도 멤버 함수와 똑같은 방식으로 참조 가능
fun Person.isAdult() = age >= 21
var predicate = Person::isAdult
'프로그래밍 노트 > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 지연 계산(lazy) 컬렉션 연산 (0) | 2020.12.13 |
---|---|
[Kotlin] 코틀린 컬렉션 함수 API(filter, map ...) (0) | 2020.12.06 |
[Kotlin] 코틀린 object/companion 클래스 (동반객체) (0) | 2020.11.30 |
[Kotlin] 코틀린 데이터 클래스와 클래스 위임 (0) | 2020.11.30 |
[Kotlin]인터페이스에 선언된 프로퍼티 구현/프로퍼티 게터 세터 (0) | 2020.11.30 |