반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 병렬프로그래밍
- Coroutine
- Compose
- 회고
- 안드로이드
- k8s
- 디자인패턴
- 알게되는
- 병럴프로그래밍
- kotlin강좌
- theming
- g 단위테스트
- 안드로이드스튜디오
- Kotlin
- 커스텀상태
- ReactiveProgramming
- viewmodel
- 테스트
- 안드로이드강좌
- 책
- 글또
- 스레드
- 코루틴
- Gradle
- android
- Rxjava
- 자바
- mockito
- 알고리즘
- 코틀린
Archives
- Today
- Total
선생님, 개발을 잘하고 싶어요.
Kotlin in Action - 2부 8장 - 고차 함수: 파라미터와 반환 값으로 람다 사용 본문
다루는 거
- 고차 함수: 람다를 인자로 받거나 반환하는 함수
- 인라인 함수
- 비로컬 return, 레이블, 로컬 return
- 무명 함수
고차함수
- 다른 함수를 인자로 받거나 함수를 반환하는 함수
- 코틀린으로 말하면, 람다나 함수 참조를 인자로 넘길 수 있거나 람다나 함수 참조를 반환하는 함수
- 코틀린에서 함수 변수 표현을 하기 위해서 람다, 함수 참조를 사용한다고 5장에서 말함.
- 인자로 받은 함수를 호출하는 구문은 일반 함수를 호출하는 구문과 같다.
- 컴파일된 코드에서 함수 타입은 일반 인터페이스로 표시된다. Function0<R>, Function1<P1, R> …
- 함수 타입 파라미터도 디폴트 값을 지정하거나 nullable 함수 타입을 만들 수 있다.
- 변수, 프로퍼티, 파라미터 등을 사용해 데이터 중복을 없애는 것과 같이, 람다를 사용해 코드의 중복을 없앤다.
- 디자인 패턴 중 전략 패턴을 구현하기 위한 인터페이스 구현 - 서브 클래싱의 구조를 탈피해서 일반 함수 타입을 사용해 전략을 표현할 수 있다.

인라인 함수: 람다의 부가 비용 없애기
람다의 부가 비용
- 코틀린이 보통 람다를 무명 클래스로 컴파일한다. 항상 그런 것은 아니고, 변수를 포획하면 람다가 생성되는 시점마다 새로운 무명 클래스 객체가 생긴다(새로운 인스턴스 생성).
- 따라서 무명 클래스 생성에 따른 부가 비용이 든다.
인라이닝 작동 방식
- 함수를 호출하는 코드를 함수를 호출하는 바이트 코드가 아니고 함수 본문을 번역한 바이트 코드로 컴파일한다.
- 이 경우 함수의 본문뿐 아니라, 인자로 전달된 람다의 본문도 함께 인라이닝된다.
- 컴파일러는 그 람다를 함수 인터페이스를 구현하는 무명 클래스로 감쌀 필요가 없다.
- 람다 대신 함수 타입의 변수를 넘긴다면, 인라인 함수 호출 시점에 변수에 저장된 람다 코드를 알 방법이 없으므로 무명 클래스로 감싸는 수밖에 없다.
한계
- 람다가 본문에 직접 펼쳐지기 때문에 함수가 람다를 본문에 사용하는 방식이 한정된다.
- 파라미터로 받은 람다를 다른 변수에 저장하고 그 변수를 추후 사용한다면, 람다를 표현하는 클래스가 어딘가에는 존재해야 하는 상황이므로 람다를 인라이닝할 수 없다.
- 둘 이상의 람다를 인자로 받는데, 일부 람다만 인라이닝하고 싶을 때는, 인라이닝하면 안 되는 람다를 noinline 변경자를 추가한다.
- 자바에서도 코틀린에서 정의한 인라인 함수를 호출할 수 있다. 이 경우 컴파일러가 인라이닝 하지 않고 일반 함수 호출로 컴파일한다.
컬렉션 연산 인라이닝
// 표준 라이브러리 활용
val people = listOf(Person("Alice", 29), Person("Bob", 31))
people.filter { it.age < 30 }
// 직접 구현
val result = mutableListOf<Person>()
for (person in people) {
if (person.age < 30) result.add(person)
}
- 이러한 코드에 대해서, 표준 라이브러리에 있는 filter가 인라인 함수이므로 위 두 구현의 바이트코드가 거의 같다.
- 고로, 코틀린다운 연산을 안전하게 사용하고, 인라이닝을 믿고 성능에 신경 쓰지 않아도 된다.
- filter, map 체이닝 시점에, 각 호출마다 새로운 리스트 객체를 만든다.
- 처리할 원소가 많아지고 중간 리스트 부가 비용을 피하기 위해서 Sequence를 사용할 수 있다.
- 하지만 Sequence는 인자로 받은 람다를 필드로 저장해서 Lazy하게 계산하므로, 인라이닝 할 수 없다.
- 고로, 오히려 크기가 작은 컬렉션은 일반 컬렉션 연산이 더 성능이 나을 수 있다.
함수를 인라인으로 선언해야 하는 경우
- inline 키워드를 사용해도 람다를 인자로 받는 함수만 성능이 좋아질 가능성이 높다.
- 일반 함수 호출의 경우 JVM은 이미 강력하게 인라이닝을 지원한다. (?!)
- 람다를 인자로 받는 함수를 인라이닝해서 얻는 이익
- 함수 호출 비용 줄일 수 있음
- 람다를 표현하는 클래스와 람다 인스턴스에 해당하는 객체를 만들 필요가 없음
- JVM은 람다를 인라이닝 해주지 못함
- 일반 람다에서는 사용할 수 없는 몇 가지 기능을 사용할 수 있음 (e.g. non-local return)
자원 관리를 위해 인라인된 람다 사용
- 람다로 중복을 업앨 수 있는 일반적인 패턴
- 자원 관리 (파일, 락, 데이터베이스, 트랜잭션)
- try/finally 문을 사용하되 try 시작 전 자원 획득 - finally에서 자원 해제
- 자바는 try-with-resource와 같은 특별한 언어 차원 구문을 제공하지만, 코틀린은 인라인 함수와 람다 인자로 인해 언어 차원으로 제공하지 않고 라이브러리로 제공함 (use)
- 자원 관리 (파일, 락, 데이터베이스, 트랜잭션)
고차 함수 안에서 흐름 제어
- 람다 안의 return문: 람다를 둘러싼 함수로부터 반환
- 넌로컬 return (non-local return): 자신을 둘러싸고 있는 블록보다 더 바깥에 있는 다른 블록을 반환하게 만듬
- 람다를 인자로 받는 함수가 인라인 함수인 경우만 가능
- 왜 그럴까? 인라이닝되지 않는 함수는 람다를 변수에 저장할 수 있다는 뜻이고, 람다가 호출되는 시점에는 이미 람다를 생성한 함수 흐름이 끝나있을 수 있다. 고로 바깥쪽 함수를 반환시키기엔 너무 늦었을 수 있다. 그렇기에 제공될 수 없다.
- 레이블을 사용한 return (람다 반환)
- 물론 람다 식에서도 local return이 가능하다.
- 로컬 return (local return): 람다의 실행을 끝내고 람다를 호출했던 코드의 실행을 이어간다.
fun lookForAlice(people: List<Person>) { people.forEach labelSomething@ { // 람다 식 앞에 레이블을 붙힌다. if (it.name == "Alice") return@labelSomething // 앞에서 정의한 레이블 참조 } } fun lookForAlice(people: List<Person>) { people.forEach { // 람다 식 앞에 레이블 지정 따로 안하면 if (it.name == "Alice") return@forEach // 인라인 함수 이름을 레이블로 사용할 수 있다. } }
- 무명 함수: 기본적으로 로컬 return
- 일반 함수와 비슷해 보이지만 실제로는 람다 식에 대한 문법적 편의
- 람다 구현 방법이나 람다 인라인 함수에 넘길 때 어떻게 인라이닝 되는지 등의 규칙은 같다.
fun lookForAlice(people: List<Person>) { people.forEach(fun (person) { // 파라미터 타입 추론되었다. fun 키워드를 붙혔다. if (it.name == "Alice") return // 기본적으로 무명 함수가 반환된다. (가장 가까운 fun) }) }
'일상 > 책 리뷰' 카테고리의 다른 글
Kotlin in Action - 2부 10장 - 애노테이션과 리플렉션 (0) | 2022.06.06 |
---|---|
Kotlin in Action - 2부 9장 - 제네릭스 (0) | 2022.05.29 |
Kotlin in Action - 2부 7장 - 연산자 오버로딩과 기타 관례 (convention) (0) | 2022.05.24 |
Kotlin in Action - 1부 6장 - 코틀린 타입 시스템 (0) | 2022.05.23 |
Kotlin in Action - 1부 5장 - 람다로 프로그래밍 (0) | 2022.05.22 |