코틀린 (Kotlin) 람다 문법

코틀린 람다 문법

코틀린 람다는 자바의 람다와 비슷하지만 문법이 조금 다릅니다.
{ x: Int, y:Int -> x+y} 처럼 블럭({..}) 안에 파라메터와 -> 를 구분자로해서 본문을 작성할 수 있습니다.

그리고 람다를 변수에 저장할 수 있습니다.

val sum = { x: Int, y:Int -> x+y} //람다식을 변수에 저장

//사용
sum(1,2)

//풀어쓴 람다
people.maxBy({ p : Person -> p.age})

//맨 뒤에 있는 인자가 람다 식인경우 밖으로 뺄 수 있다
people.maxBy() {
    p : Person -> p.age
}

//람다가 유일한 인자인경우 괄호 생략 가능
people.maxBy {
    p : Person -> p.age
}

아래 코드는 파라메터가 두개 이상이고 마지막 하나만 람다인 경우입니다.

people.joinToString(" ") { p: Person -> p.name } //마지막 람다를 블럭밖으로 뺌

람다 파라미터에서 타입을 제거할 수 있습니다.

//파라미터 타입 명시
people.maxBy { p:Person -> p.age }

//파라미터 타입 추론(컴파일러가 추론)
people.maxBy { p -> p.age }

람다의 파라미터가 하나이고 컴파일러가 타입을 추론할 수 있는 경우 it 을 사용할 수 있습니다.

//it 사용 : 람다의 파라미터가 하나인경우 컴파일러가 추론 가능
//중첩된 람다에서는 it 보다는 파라미터를 명시하는 것이 좋다
people.maxBy { it.age }

람다를 변수에 저장할 때는 타입을 추론할 수 없기때문에 타입을 명시해야 됩니다.

//람다를 변수에 저장할때는 추론할수 없기때문에 파라미터를 명시한다
val getAge = { p:Person -> p.age }
people.maxBy(getAge)

람다 블럭의 마지막에 있는 식이 리턴되는 결과값이 됩니다.

val sum = { x:Int, y:Int ->
    x+y //마지막 식이 리턴
}
sum(1,2) // 3이 리턴 됨

함수안에 내부 클래스를 정의할 경우 내부 클래스에서 함수의 파라메터를 접근해야되는 경우 final 이 되야합니다.

람다에서는 final 을 붙이지 않습니다.
람다는 모든 참조를 래퍼로 감싸서 읽고 쓸수 있도록 람다 코드와 함께 저장되기 때문입니다.
람다는 함수의 파라메터와 로컬 변수까지 래퍼로 감싸집니다.

val errors = listOf("403", "404")

fun printMessageWithPrefix(messages: Collection, prefix: String) {
    messages.forEach { //람다식을 파라메터로 받는 forEach 함수


        L.d("$prefix: $it") //파라메터 접근($prefix)
    }
}

fun printProblemCount(responses: Collection) {
    var clientErros = 0
    var serverErrors = 0


    responses.forEach {
    
    if (it.startsWith("4")) {
            clientErros++ //로컬 변수 수정

        } else if (it.startsWith("5")) {
            serverErrors++ //로컬 변수 수정
        }
    }
}

보통 로컬 변수는 함수 실행이 종료되면 해제됩니다.
그러나 람다를 변수에 저장할 경우에는 람다식이 로컬 변수를 래핑하기 때문에 해재되지 않고 존재합니다.

그러나 아래 코드처럼 click을 바로 리턴하면 onClick에서 clicks++ 해도 이미 click 이 리턴됬기때문에 항상 0을 리턴합니다.

fun tryToCountButtonClicks(button: Button): Int {
    var clicks = 0
    button.setOnClickListener {
        //clicks 는 return 된후 onClick에 의해 clicks가 증가되기때문에 clicks 는 0 임
        clicks++
    }
    return clicks
}

멤버참조

코틀린에서 :: 를 사용해서 프로퍼티나 메소드를 참조해서 값으로 저장할 수 있습니다.

people.maxby(Person::age)     //멤버참조
people.maxby { p -> p.age }   //람다로 풀어쓴 경우
people.maxBy { it.age }          //람다로 간략하게 사용한 경우

//최상위 함수, 프로퍼티 참조
fun salute() = L.d(">>>")
run(::salute) //run함수에 참조 전달

//람다 대신 멤버 참조
val nextAction = :: salute

//생성자 참조
data class Person(val name: String, val age: Int)
val p = ::Person //생성자 참조
val person = p("zerog", 20)

fun constructorFunc(twoargs : (String, Int) -> Person) {
    val p = twoargs("zerog", 10)
}
constructorFunc(::Person)

//확장 함수 참조
fun Person.isAdult() = age>=20
val predicate = Person::isAdult

자바 메소드에 코틀린 람다식 넘기기

일반적으로 자바에 객체 식을 넘길때는 obejct 를 사용합니다.

//Runable 를 인자로 받는 postponeComputation(int delay, Runnable run) 함수가 있음
val ramdaJava = RamdaJava()

//프로그램 전체에서 Runnable의 인스턴스는 단 하나만 생성
for (i in 1..3) {
    ramdaJava.postponeComputation(100) {
        L.d("run") //인자 포획 없음
    }
}

//메소드를 호출할때마다 object 가 새로 생성된다
for (i in 1..3) {
    ramdaJava.postponeComputation(100, object : Runnable {
        override fun run() {
            L.d("run")
        }
    })
}

//전역변수인 runnable 변수는 하나만 생성되고 모든 호출에 같은 전역변수 runnable을 사용한다
for (i in 1..3) {
    ramdaJava.postponeComputation(100, runnable)
}

//변수를 포획한다면 호출마다 새로 생성된다
for (i in 1..3) {
    ramdaJava.postponeComputation(100) {
            L.d("$i") //포획
        }}
}


람다와 리스너 등록과 해제하기

람다는 코드 블록이기 때문에 this 는 람다를 둘러싼 클래스를 가리킵니다.
그러므로 람다 안에서 this 를 사용해서 리스너를 해제해야될 경우에는 object 무명 객체를 사용해야 됩니다.

textView.viewTreeObserver.addOnPreDrawListener(
    object : ViewTreeObserver.OnPreDrawListener {
        override fun onPreDraw(): Boolean {
            logTextView.viewTreeObserver.removeOnPreDrawListener(this)
            return false
        }
})



*개인적으로 코틀린을 공부하면서 정리한 자료입니다. 수정 사항 및 이슈가 있는 경우 메일 부탁드립니다.

댓글

이 블로그의 인기 게시물

코틀린 (Kotlin) filter, map, all, any, count, find, groupBy, flatMap 함수 정리

코틀린 (Kotlin) 인터페이스 정리

RecyclerView 에서 notifyItemChanged()의 payload 이해하기