일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 코틀린
- 코루틴
- 테스트
- 병럴프로그래밍
- 책
- 자바
- viewmodel
- theming
- 회고
- 스레드
- Kotlin
- g 단위테스트
- 안드로이드스튜디오
- 알고리즘
- kotlin강좌
- 디자인패턴
- 안드로이드
- 글또
- mockito
- Gradle
- 알게되는
- 안드로이드강좌
- 커스텀상태
- ReactiveProgramming
- Compose
- k8s
- 병렬프로그래밍
- android
- Rxjava
- Coroutine
- Today
- Total
선생님, 개발을 잘하고 싶어요.
Kotlin in Action - 1부 5장 - 람다로 프로그래밍 본문
다루는 거
- 람다
- 멤버 참조
- 컬렉션 라이브러리
- 코틀린 시퀀스
- 함수형 인터페이스, SAM 인터페이스
- 수신 객체 지정 람다 (with, apply)
행동을 변수로 저장하거나 함수 인자로 넘기기: 람다(lambda) 와 멤버 참조(member reference)
람다
코틀린은 함수를 값처럼 다루는 방식을 선택했다. 함수를 직접 다른 함수에 전달할 수 있다.
예시로 컬렉션을 다룰 때 수행하는 대부분의 작업은 몇 가지 일반적 패턴에 속하고 이 패턴은 라이브러리로 제공된다. 컬렉션으로 수행하는 일반적인 알고리즘을 구성하고 람다를 인자로 받아서 재활용할 수 있게 되었다.
람다나 멤버 참조를 인자로 받는 함수를 통해 개선한 코드는 더 짧고 더 이해하기 쉽다.
people.maxBy { it.age } // 람다로 넘기기
people.maxBy(Person::age) // 멤버 참조로 넘기기
람다는 값처럼 여기저기 전달할 수 있는 동작의 모음이다.
몇 가지 유용한 규칙이 있다.
- 함수 호출 시 맨 뒤에 있는 인자가 람다 식이라면 그 람다를 괄호 밖으로 빼낼 수 있다.
- people.maxBy() { p: Person -> p.age } // 가능
- 람다가 어떤 함수의 유일한 인자이고 괄호 뒤에 람다를 썼다면 호출 시 빈 괄호를 없애도 된다.
- people.maxBy { p: Person -> p.age } // 빈 괄호는 없애도 괜찮다.
- 컴파일러가 람다 파라미터 타입을 추론할 수 있는 경우는 생략해도 괜찮다.
- people.maxBy { p -> p.age } // people이 List<Person> 타입인 걸 컴파일러가 알고있다.
- 람다의 파라미터가 하나뿐이고 그 타입을 컴파일러가 추론할 수 있는 경우 it을 바로 쓸 수 있다.
- people.maxBy { it.age } // 인자가 하나니까 괜찮다.
람다 파라미터뿐 아니라 람다 정의 앞에 선언된 로컬 변수까지 람다에서 모두 사용할 수 있다.
자바 람다와 다르게 코틀린 람다에선 파이널 변수가 아닌 변수에 접근할 수 있다. 이는 Ref<T>라는 별도 파이널 인스턴스를 활용해 자바 람다의 규칙을 우회했기 때문에 가능하다.
람다 안에서 사용하는 외부 변수를 람다가 포획한 변수 (capture)라고 부른다.
멤버 참조
:: 를 사용하는 식을 멤버 참조라고 부른다.
멤버 참조는 그 멤버를 호출하는 람다와 같은 타입이다. 혼용가능하다는 뜻
람다 인자가 여럿인 다른 함수한테 작업을 위임하는 경우 위임 함수에 대한 참조를 제공하면 편리하다.
val action = { person: Person, message: String ->
sendEmail(person, message)
}
val nextAction = ::sendEmail // 단순 위임의 경우 람다보다 멤버 참조가 더 편리하다.
특정 인스턴스 instance::field 와 같은 문법도 제공되곤 하는데 이는 바운드 멤버 참조(bound member reference)라고 한다. 클래스 인스턴스를 함께 저장한 다음 그 인스턴스에 대한 멤버를 호출해주는 것이다.
컬렉션 함수형 API
람다나 멤버 참조를 통해서 동작을 함수 인자로 넘길 수 있으니, 이를 활용해서 많은 라이브러리 코드가 제공된다.
filter: 조건식이 일치하는 원소만 반환
map: 주어진 람다를 각 원소에 적용한 결과 리스트 반환
all: 모든 원소가 조건을 만족하는지
any: 하나의 원소라도 조건을 만족하는지
count: 조건을 만족하는 원소가 몇 개인지
find: 조건을 만족하는 첫 원소가 무엇인지
flatMap: 인자로 주어진 람다를 컬렉션의 모든 객체에 적용(map) 후 결과를 하나의 리스트로 반환(flatten)
flatten: 결과를 하나의 리스트로 풀어 반환
지연 개산 컬렉션 연산 (feat. Sequence)
컬렉션 함수를 연쇄하면 매 단계마다 계산 중간 결과를 새로운 컬렉션에 임시로 담는다.
시퀀스를 사용하면 중간 임시 컬렉션을 사용하지 않고도 컬렉션 연산을 연쇄할 수 있다.
원소가 매우 많은 경우 컬렉션 연쇄는 새로운 리스트를 만든다는 점에서 오버해드가 크다.
시퀀스의 경우 모든 연산은 각 원소에 대해 순차적으로 적용된다.
자바 8 스트림에서 제공하는 것과 코틀린 시퀀스는 별개로 만들어졌는데 이는 코틀린이 자바 6을 지원하기 때문이다. 자바 8 스트림에서 제공하는 CPU 병렬적 실행등의 기능을 활요하고 싶다면 자바 8 스트림을 선택해야한다.
generateSequence를 통해서 시퀀스를 만들 수 있다.
자바 함수형 인터페이스
자바에서 람다가 없을 때 동작을 넘기기 위해서는 함수형 인터페이스 (SAM 인터페이스, Single Abstact Method)를 무명 클래스 인스턴스로 만들어 넘기곤 했다.
코틀린은 대신 람다를 넘길 수 있다.
자바 메서드에 람다를 인자로 전달하면, 컴파일러가 자동으로 람다를 인스턴스로 변환해준다.
인라인 되지 않은 람다 식은 무명 클래스로 컴파일된다. 그 반대로 inline 코틀린 함수에 람다를 넘기면 무명 클래스를 만들지 않는다.
대부분의 경우 람다와 자바 함수형 인터페이스 사이 변환은 자동으로 이루어진다. 그렇지 않은 경우는… 자동으로 생성된 SAM 생성자를 활용하면 된다.
Runable { ... } // Runable 이름을 가진 생성자 함수가 자동 생성되었다.
람다와 다르게 무명 객체 안에서는 this가 그 무명 객체 인스턴스 자신을 가리킨다.
수신 객체 지정 람다: with, apply
일반 함수 - 람다
확장 함수 - 수신 객체 지정 람다
'일상 > 책 리뷰' 카테고리의 다른 글
Kotlin in Action - 2부 7장 - 연산자 오버로딩과 기타 관례 (convention) (0) | 2022.05.24 |
---|---|
Kotlin in Action - 1부 6장 - 코틀린 타입 시스템 (0) | 2022.05.23 |
Kotlin in Action - 1부 4장 - 클래스, 객체, 인터페이스 (0) | 2022.05.21 |
Kotlin in Action - 1부 3장 - 함수 정의와 호출 (0) | 2022.05.18 |
Kotlin in Action - 1부 2장 - 코틀린 기초 (0) | 2022.05.17 |