테코톡을 신청하면서 kotlin의 scope function들을 주제로 잡았는데,
나는 lambda가 뭔지도 모르는데 이걸 발표한다고 한거였다 ㅋ_ㅋ
그래서 또 건너 뛰어서 ~ 읽어보는 ~ Kotlin In Action 5장. 람다로 프로그래밍 입니다 🤗
기본적으로 다른 함수에 넘길 수 있는 작은 코드 조각을 뜻한다.
람다를 사용하면 공통 코드 구조를 쉽게 라이브러리 함수로 뽑아낼 수 있다.
이전에 자바에서는 무명 내부 클래스를 통해 일련의 동작을 변수에 저장하거나 다른 함수에 넘겨야 하는 작업들을 해결했었다. 무명 내부 클래스를 사용하면 코드를 함수에 넘기거나 변수에 저정할 수 있기는 하지만 상당히 번거롭다.
이와 달리 함수형 프로그래밍에서는 함수를 값처럼 다루는 접근 방법을 택해 이러한 문제를 해결한다. 클래스를 선언하고, 그 클래스의 인스턴스를 함수에 넘기는 대신 함수형 언어에서는 함수를 직접 다른 함수에 전달할 수 있다.
람다 식을 이용하면 코드가 더욱 간결해진다.
람다 식을 사용하면 함수를 선언할 필요가 없고 코드 블록을 직접 함수의 인자로 전달 가능하다.
button.setOnClickListener (new OnClickListener () {
@Override
public void onClick(View view) {
/*동작*/
}
});
위와 같이 무명 내부 클래스를 선언하느라 번잡스러워진 코드를 람다를 사용하려 클릭 시 동작을 간단히 기술할 수 있다.
button.setOnClickListener { /*동작*/ }
이전 자바에서는 람다가 존재하지 않아 컬렉션을 편리하게 처리할 수 있는 라이브러리를 제공하기 힘들었고, 필요한 기능들을 직접 작성하는 경우가 많았다.
만약 람다를 사용하지 않는다면, 사람들 중 연장자를 찾기 위해서 최고령과 연장자 변수를 선언하고 루프를 돌렸을 것이다.
하지만 람다식을 사용하여 라이브러리 함수를 활용하면 다음과 같이 한줄로 끝낼 수 있다.
data class Person(val name: String, val age: Int)
val people = listOf(Person("ringring", 26), Person("movie", 26))
people.maxBy { it.age }
// 이런식으로 단지 함수나 프로퍼티를 반환하는 역할을 수행하는 람다는 멤버 참조로 대치할 수 있다.
people.maxBy(Person::age)
{ 인자 목록 -> 람다 본문 }
people.maxBy({ p: Person -> p.age })
// 코틀린에서는 함수 호출 시 맨 뒤에 있는 인자가 람다 식이라면 그 람다를 괄호 밖으로 빼낼 수 있다.
people.maxBy() { p: Person -> p.age }
// 람다가 어떤 함수의 유일한 이자이고, 괄호 뒤에 람다를 썼다면 호출 시 괄호를 없애도 좋다.
people.maxBy { p: Person -> p.age }
// 컴파일러는 람다 파라미터의 타입도 추론 가능하기 때문에 파라미터 타입을 명시할 필요가 없다
people.maxBy { p -> p.age }
people.joinToString(separator = " ", transform = { p: Person -> p.name }
people.joinToString(" ") { p: Person -> p.name }
자바에서와 달리 코틀린의 람다는
포획한 변수가 있는 람다를 저장해서 함수가 끝난 뒤에 실행해도 람다의 본문 코드는 여전히 포획한 변수를 읽거나 쓸 수 있다.
HOW?
주의
람다를 이벤트 핸들러나 다른 비동기적으로 실행되는 코드로 활용하는 경우 함수 호출이 끝난 다음 로컬 변수가 변경될 수도 있다.
fun tryToCountButtonClicks(button: Button): Int {
var clicks = 0
button.onClick { clicks++ }
return clicks
}
위의 함수는 항상 0을 반환한다. onClick 핸들러는 호출될 떄마다 clicks 값을 증가시키지만 그 값을 변경을 관찰 할 수 없다. 핸들러는 함수가 clicks가 반환된 후 호출되기 때문이다.
-> 제대로 구현하기 위해서는 카운터 변수를 함수의 내부가 아니라 클래스의 프로퍼티나 전역 프로퍼티 등의 위치로 빼내서 나중에 변수의 변화를 살펴볼 수 있게 해야한다.
멤버 참조는 프로퍼티나 메서드를 단 하나만 호출하는 값을 만들어준다.
::는 클래스 이름과 참조하려는 멤버(프로퍼티나 메서드) 이름 사이에 위치한다.
people.maxBy(Person::age)
people.maxBy { p -> p.age }
people.maxBy { it.age }