일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |
- 디자인패턴
- 함수형프로그래밍
- 코틀린
- Kotlin
- Functional Programming
- PrototypePattern
- 프로토타입 패턴
- Abstract Factory
- ㅓ
- builderPattern
- Design Pattern
- F
- 팩토리 메소드
- 옵저버 패턴
- r
- Observer Pattern
- designPattern
- ㅋㅁ
- El
- 디자인패턴 #
- Singleton
- a
- 추상 팩토리
- factory method
- 빌터패턴
- 싱글톤
- 추상팩토리패턴
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
인라인 함수: 람다의 부가 비용 없애기 본문
코틀린의 람다
- 무명 클래스로 컴파일 되지만, 사용할때 마다 새로운 클래스가 생성되는것은 아님
- 람다함수 안에서 변수를 포획시, 람다가 생성되는 시점마다 새로운 무명클래스 객체가 생긴다. (실행시점 무명클래스 생성에 부가 비용)
반복되는 코드를 별도의 라이브러리 함수로 빼내되 컴파일러가 자바의 일반 명령문만큼 효율적인 코드를 생성하게 해보자
inline 키워드를 사용하면 그 함수의 본문이 인라인이 된다.
함수를 호출하는 바이트 코드 대신에 함수의 본문을 번역한 바이트 코드로 번역된다.
예제) 다중 스레드 환경에서 공유자원에 대한 동시접근을 막기 위한 코드
inline fun <T> synchronized(lock: Lock, action: () -> T): T{
lock.lock()
try {
return action()
}
finally {
lock.unlock()
}
}
lock을 잠그고 특정 로직을 수행후 다시 lock을 해제한다.
코틀린 표준 라이브러리는 어떤 타입의 객체든 인자로 받을 수 있는 synchronized 함수를 제공한다.
이 함수는 inline을 선언했으므로 자바의 synchronized문과 같아진다
fun foo(l: Lock){
println("Before sync")
synchronized(l){
println("action")
}
println("after sync")
}
synchronized 함수뿐 아니라 인자로 넘겨주는 람다 역시 inline 된다.
action을 호출하는 synchronized의 일부분으로 간주되어 함수인터페이스를 구현하는 무명클래스로 생성하지 않는다.
아래 코드는 위의 코드와 동일한 바이트 코드를 생성한다.
fun __foo__(l: Lock){
println("Before sync")
lock.lock()
try {
println("action") // 람다 본문이 inline 처리됨
}
finally {
lock.unlock()
}
println("after sync")
}
인라인 함수를 호출하면서 람다 대신 함수타입의 변수를 넘기게 되면,
class LockOwner(val lock: Lock){
fun runUnderLock(body: () -> Unit){
synchronized(lock , body)
}
}
synchronized 함수를 호출하는 코드위치에서 변수에 저장된 람다 코드가 무엇인지 알수없다.
그래서 람다는 인라인 되지 않고 synchonized 함수만 인라인 된다.
class LockOwner(val lock: Lock) {
fun __runUnderLock__(body: () -> Unit) {
lock.lock()
try {
body()
} finally {
lock.unlock()
}
}
}
1개의 인라인 함수를 서로 다른곳에서 각각 다른 람다 값을 전달해 호출한다면, 그 두 호출은 서로 따로 인라인된다.
인라인함수의 본문 코드가 호출시점에 인라인 되고, 각 람다의 본문이 인라인 함수의 본문코드에서 람다를 사용하는 위치에 복사된다
인라인 함수의 한계
인라인을 하는 방식으로 인해 람다를 사용하는 모든 함수를 인라이닝할 수는 없다.
함수가 인라이닝될 때 그함수에 인자로 전달되는 람다식의 본문은 결과 코드에 직접들어 갈 수 있다.
이렇게 람다 마다가 본문에 직접복사 되기에 함수가 파라미터로 람다를 받아서 호출한다면, 사용방식이 제한이된다.
inline fun foo( 람다 ) { 람다 호출(람다 본문으로 대체) }
하지만 람다를 다른 변수에 저장하고 나중에 그 변수를 사용한다면 라다를 표한하는 객체가 어딘가에 존재해야되서 불가능하다.
inline fun foo( 람다 ) {
val a = 람다
...
a( ) // 저장후 나중에 사용하기 위해서는 객체가 필요하기 때문에 인라이닝 불가능하다.
}
컴파일러는 illegal usage of inline-parameter 라는 메세지와 함께 금지킨다.
시퀀스에 대해 동작하는 메소드 중에서는 람다를 받아서 모든 시퀀스 원소에 적용시킨 새로운 시퀀스를 반환한다.
fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R>{
return TransFormingSequence(this, transform)
}
transform을 파라미터로 전달받아 호출하지 않고 TransFormingSequence의 생성자에게 그 함수를 넘겨준다.
TransFormingSequence 생성자에서 전달 받은 함수를 프로퍼티로 저장한다. 이런경우 transform을 함수 인터페이스를 구현하는 무명클래스 인스턴스로 생성해야된다.
두개 이상의 람다를 파라미터로 받는 인라인 함수는 특정함수만 인라인 처리 하고 나머진 인라인 처리를 하지 않고 싶을수 있다.
inline fun foo(inlined: () -> unit, noinline notInlined: () -> Unit){ ....}
자바에서 코틀린의 인라인 함수를 호출할때 인라이닝 하지 않고 일반함수 호출로 컴파일한다.
'Kotlin in Action > 코틀린 답게 사용하기' 카테고리의 다른 글
고차 함수 안에서 흐름 제어 (0) | 2021.02.26 |
---|---|
컬렉션 연산 inline (0) | 2021.02.25 |
고차 함수 : 파라미터와 반환 값으로 람다 사용하기. (0) | 2021.02.24 |
연산자 오버로딩과 기타 관례(위임 프로퍼티 기타) (0) | 2020.08.25 |
연산자 오버로딩과 기타 관례(위임 프로퍼티 기타) (0) | 2020.08.25 |