이 글은 Kotlin In Action을 참고 하였습니다.
더욱 자세한 설명이나 예제는 직접 책을 구매하여 확인 하시기 바랍니다
코틀린의 람다는 자바8의 람다와 개념은 매우 비슷합니다.
다만 표현하는 방식이 살짝 다를 뿐이라서 자바8의 기본적인 람다 사용법에 대한 이해가 있다는 가정하에 설명합니다.
자바의 lambda에 대한 글은 http://tourspace.tistory.com/3?category=788398 를 먼저 읽어보고 오길 추천드립니다.
람다식은 자바8부터 사용이 가능합니다.
또한 안드로이드에서 제대로된 functional interface를 이용하려면 N OS 이상이어야만 합니다.
코틀린에서는 위 제한과 상관없이 사용할 수 있다는 점이 가장 큰 매력이 아닐까 싶습니다.
(한 4년쯤 지나고 나면 아마도 모두 자바8이상과 min sdk를 N OS 이상으로 쓰고 있지 않을까 싶습니다만..ㅎㅎ)
추가적으로 람다식은 기본적인 편리한 lambda api들을 제공합니다.
IDE의 어떤한 로직을 구현할때 자동완성기능을 이용하여 먼저 해당 api를 찾아보고 사용한다면 훨씬더 간결하고 직관적인 코드를 짤 수 있습니다.
5.1.2 lambda and collection
data class Person(val name: String, val age: Int)
fun findTheOldest(people: List) {
var maxAge = 0
var theOldest: Person? = null
for (person in people) {
if (person.age > maxAge) {
maxAge = person.age
theOldest = person
}
}
println(theOldest)
}
fun main(args: Array) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
findTheOldest(people)
}
Person이란 객체를 만들고 가장 나이가 많은 한사람을 뽑는 코드 입니다.
사실 구현하기엔 어렵지 않으며, 너무나도 많이 사용하는 코드 패턴입니다.
fun main(args: Array) {
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.maxBy {it.age})
}
코틀린에는 maxBy란 collection의 확장함수를 제공합니다.
따라서 위 코드처럼 findTheOldest()를 구현하지 않고도 간단하게 한줄로 표현할 수 있습니다.
5.1.3 Lambda expression
fun main(args: Array) {
val sum = { x: Int, y: Int -> println("Computing the sum of $x and $y...")
x + y
}
println(sum(1, 2))
}
- 중괄호로 감싼다. { .. }
- 인자와 본문은 ->로 구분한다.
- 인자는 ()로 감싸지 않는다.
- 인자는 형식추론이 가능하므로 타입을 생략할 수 있다.
- 변수에 람다식을 담는경우에는 인자의 타입을 생략할 수 없다.
val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2))
{ println(42) }()
run { println(42) }
람다식을 변수에 넣어 호출할 수도 있지만 {} 함수내에 넣고 ()를 이용하여 바로 실행할 수도 있습니다.
단 이때는 run keyword를 사용하는게 가독성에 더 좋습니다.
코드의 간결성을 위해 아래와 같은 규칙도 존재합니다.
- 함수의 맨 마지막 인자가 람다라면 () 안에서 빼내서 밖에 람다를 표현할 수 있다.
- 인자가 하나라면 그 인자는 람다식 내부에서 it으로 받을 수 있다.
- 인자가 하나이면서 그 인자가 람다타입 이라면 ()를 생략할 수 있다.
people.maxBy ({p: Person -> p.age})
people.maxBy () {p: Person -> p.age}
people.maxBy {p: Person -> p.age}
people.maxBy {it.age}
위 네가지의 코드는 모두 같은 표현입니다.5.1.4 Lambda의 closure
fun lambdaSample() {
var counter = 0
val inc = {counter++}
run {println(Inc)}
}
counter는 var로 final 변수가 아니지만 위 코드는 정상 동작 합니다.
위 코드는 사실 아래와 같이 class로 wrapping되어 실행 됩니다.
class Ref<T>(var value: T)
fun lambdaSample() {
val counterWrapper = Ref(0)
val inc = {counterWrapper.value++}
run {println(Inc)}
lambda의 closure에 동작방식에 대해 이해 했다면, 아래 코드의 return 값은 항상 0 이란걸 이해해야 합니다.
(그렇지 않으면, 나중에 람다식의 잘못된 closure 사용으로 코드가 원하는대로 동작하지 않을 수 있습니다.)
fun clickCount(button: Button): Int {
var clicks = 0
button.onclick { clicks++ }
println(clicks)
}
5.1.2 member reference
::를 사용하여 표현하는 방법은 총 네가지 입니다.
5.1.2.1 클래스의 멤버 표현
people.maxBy {Person::age}
표현식) 클래스이름::멤버변수
5.1.2.2 최상위 함수의 표현
// 최상위 함수
fun showYourName() = println("HongGilDong")
run(::showYourName)
표현식) ::최상위함수
5.1.2.3 변수에 람다대신 member reference 저장
val action1 = {person:Person, msg:String -> sendMail(person. msg)}
val action2 = ::sendMail
표현식) ::함수
data class Person(val name: String, val age: Int)
fun main(args: Array) {
val createPerson = ::Person
val p = createPerson("Alice", 29)
println(p)
}
생성자를 변수에 저장할 수 있고, 생성을 지연(late initialize) 시킬수 있습니다.5.1.2.5 Extension function
확장함수도 람다식을 이용하여 표현할 수 있습니다.
//확장함수
fun Person.isAdult() = this.age >= 20
val predicate = Person::isAdult
표현식) 클래스::함수
5.1.2.6 bound member
val p = Person("Kim", 37)
val ageFun1 = Person::age
println(ageFun1(p)) // 기본 receiver 객체 필요
val ageFun2 = p:age
println(ageFun1()) // 기본 receiver 객체 불필요 (바운드 멤버 참조)
표현식) 객체::함수
'개발이야기 > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 람다#3 Functional Interface, SAM, apply, with, buildString (2) | 2018.04.26 |
---|---|
[Kotlin] 코틀린 람다 #2 Collection API - filter, map, groupby, flatmap, sequence, find, any, all, count (2) | 2018.04.25 |
[Kotlin] 코틀린 object (6) | 2018.04.23 |
[kotlin] 코틀린 데이터 클래스와 위임 (0) | 2018.04.22 |
[Kotlin] 코틀린 class 생성자와 property (0) | 2018.04.20 |