반응형
kotlin에서의 람다식과 멤버 참조 (lambda, ::, 값으로 표현된 함수)
- 람다
- 다른 함수에 넘길 수 있는 작은 코드 조각
- 쉽게 공통 코드 구조를 라이브러리 함수로 뽑아낼 수 있다
- 람다 주 사용처
- 컬렉션 처리
- 람다 식은 java 에도 있다 (버전 8부터)
😎 람다 소개 : 코드 블록을 함수 인자로 넘기기
- 람다 이전의 방식
- 무명 내부 클래스 사용 (java)
// 이름이 없는 클래스 (watchTV) 예시
abstract class TV {
public abstract void powerOn();
}
public class Test{
public static void watchTV(TV tv){
tv.powerOn();
}
public static void main(String[] args){
watchTV(new TV(){
public void powerOn(){
System.out.println("Power On");
}
});
}
}
- 무명 내부 클래스
- 클래스 안에서 구현 된 클래스
- TV 클래스를 상속한 내부 클래스가 만들어지게 된다
- 이름을 갖는 클래스의 경우에
class NamedClass extends TV
이렇게 구현 - 무명내부 클래스를 사용하면 코드를 함수에 넘기거나 변수에 저장할 수 있다
- 함수를 값처럼 다루는 방식
- 함수형 프로그래밍에서 사용
- 함수를 직접 다른 함수에 전달하는 방식
💩 kotlin collection을 lambda 식으로 다루기
val people = listOf(Person("Alice", 29), Person("Bob", 31))
println(people.maxBy{it.age})
println(people.maxBy(Person::age))
- kotlin에서는 모든 컬렉션에 대해 maxBy 함수를 호출할 수 있다
- maxBy 함수의 인자
- 가장 큰 원소를 찾을때 비교에 사용할 값을 인자로 받는다
- Person::age
- 멤버 참조를 사용하여 컬렉션 검색하기
🤖 람다 식의 문법
- 람다식은 항상 중괄호 사이에 위치한다
- ->
- 인자 목록과 람다 본문을 구분해 준다
- 오른쪽 : 람다 본문
val sum = {x: Int, y: Int -> x+y} println(sum(1,2))
- 람다식을 sum이라는 변수에 저장하여 함수처럼 사용가능
- { } 중괄호에 묶인 것은 람다식 (일반 함수로 사용가능) 이 되어 바로 호출도 가능하다
{println(2)}()
- run을 사용해서 람다식을 실행할 수 있다
run {println(2)}
people.maxBy{p : Person -> p.age}
- 정식으로 람다식 사용했을때의 모습
- 위 코드의 문제점
- 구분자가 많아 낮은 가독성
- 컴파일러가 문맥으로부터 유추할 수 있는 인자타입 명시
- 코틀린에서는 함수 호출 시, 맨 마지막 인자가 람다식이면 그 람다를 괄호 밖으로 뺄 수 있다
- `people.maxBy(){p:Person -> p.age}
- 그 함수의 유일한 인자가 람다식이라면 괄호도 생략 가능하다
- `people.maxBy{p:Person -> p.age}
- 이렇게 사용을 할때, 마지막 인자가 어떤 인자인지 이름도 생략되기 때문에 함수에 익숙하지 않은 사람에게는 오히려 더 어려울 수 있다
- intelliJ 꿀팁
- 람다식을 괄호 밖으로 이동하기 / 람다식을 괄호 안으로 이동하기 두가지 옵션이 있다
- 컴파일러는 람다 파라미터의 타입도 추론할 수 있다
people.maxBy{p -> p.age}
- 컴파일러가 람다 파라미터의 타입을 추론하지 못하는 경우도 있을 수 있다
- 람다를 변수에 저장할 때는 파라미터의 타입을 추론할 문맥이 존재하지 않는다
val getAge = {p -> p.age}
- 위 코드 에러
- p: Person 명시해야 한다
- 여러 파라미터중에 몇개만 타입을 명시하는것도 가능
- it 으로 파라미터 표현
- 파라미터가 하나뿐일때만 사용 가능한가?
people.maxBy{it.age}
- 람다 안에 람다가 중첩되는 경우 각 람다의 파라미터를 명시해줄 것
- 본문이 여러 줄로 이루어진 람다식
- 본문의 맨 마지막에 있는 식이 람다의 결과값
🥴 kotlin lambda 의 변수 capture
- java의 무명 내부 클래스
- 메소드의 로컬 변수를 무명내부 클래스에서 사용할 수 있었다
- 람다를 함수 안에서 정의한 경우에 함수의 파라미터 뿐만 아니라 람다 정의의 앞에 선언된 로컬 변수까지 사용 가능
fun printMessageWithPrefix(messages : Collection<String>, prefix : String){ messages.forEach{ println("$prefix $it") } } val errors = listOf("403 Forbidden", "404 Not Found") printMessageWithPrefix(errors, "ERROR : ")
- List와 Set은 Collection을 상속한다
- List, Set과 같은 클래스들은 Collection에 대입할 수 있기 때문에 messages 저런식으로 사용가능
- kotlin에서는 자바와 다르게 람다 안에서 파이널 변수가 아닌 변수에 접근할 수 있다?
- java에서는 static 변수에만 접근할 수 있었나?
- 람다 안에서 바깥의 변수를 변경해도 된다
- 함수 내부 로컬 변수의 생명주기
- 보통은 함수가 반환되면 해당 변수의 생명주기도 끝난다
- kotlin에서 함수가 로컬변수를 포획한 람다를 반환하거나 다른 변수에 저장하는 경우 생명주기가 달라질 수 있다
fun tryToCountButtonClicks(button : Button):Int{ var clicks = 0 button.onClick {clicks++} return clicks }
- 람다를 이벤트 핸들러나 다른 비동기적으로 실행되는 코드로 활용시 유의
- 함수 호출이 끝난 다음에 로컬 변수가 변경될 수도 있다
- 위 함수는 항상 0을 반환한다
- 해결방법
- clicks 변수를 함수 외부로 빼야 한다
- 해결방법
😧 멤버 참조 ::
- 함수를 값으로 바꾸기
val getAge = Person::age
- ::
- 멤버참조
- 프로퍼티나 메소드를 단 하나만 호출하는 함수 값을 만들어준다
- 함수, 프로퍼티 상관없이 :: 뒤에 괄호를 넣으면 안된다
fun salute() = println("SALUTE") run (::salute)
- 최상위 함수를 참조
- run은 인자로 받은 함수를 호출한다
val action = {person:Person, message:String -> sendEmail(person, message)} val nextAction = ::sendEmail
- person, message 두개의 인자를 가진 람다가 sendEmail 함수에 작업을 위임한다
- ::sendEmail
- 람다 대신 멤버 참조를 쓸 수 있다
- 그러면 최상위 멤버 참조이니 같은 파일에 person, message가 있는것인가?
- 확장함수도 멤버함수와 같은 방식으로 참조 가능
Reference
반응형
'Languages' 카테고리의 다른 글
kotlin의 lazy evaluation (sequence, map, filter) (0) | 2020.04.26 |
---|---|
kotlin의 collection 관련 함수 (filter, map, groupby ,flatMap, 술어함수) (0) | 2020.04.26 |
kotlin in Action 4장 예제 (by, selaed, factory method, constructor) (0) | 2020.04.26 |
kotlin의 가시성 (open, final, abstract), 생성자 (0) | 2020.04.19 |
Kotlin의 함수 (Collection, 확장함수, 가변 인자 함수, 중위호출) (5) | 2020.04.16 |