일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Collection
- pushnamed
- python
- 다트
- DART
- function
- 플러터
- List
- package
- 파이썬
- kotlin
- texttheme
- text
- 콜렉션
- import
- Android
- 크롤러
- variable
- textstyle
- Class
- Flutter
- crawler
- 코틀린
- 함수
- ML
- 웹크롤러
- set
- 클래스
- animation
- map
- Today
- Total
조용한 담장
코틀린(Kotlin) : 인라인 함수 (Inline Functions) 본문
코틀린(kotlin) 의 인라인 함수 (inline function) 을 살펴보자.
원문 https://kotlinlang.org/docs/reference/inline-functions.html 을 보며 정리.
고차함수(higher-order functions) 를 사용한다는 것은 어느정도의 런타임 패널티를 가지게 되는 셈이다.
고차함수에서 사용되는 각 함수는 object 가 되며 closure 를 가지고 있게 되는데 이는 함수의 body 내에서 접근되는 변수들도 가지고 있다는 말이 된다.
함수 오브젝트와 클래스를 위해 메모리를 할당하고 가상 호출(virtual call) 이 일어나는 것은 런타임 오버헤드를 가진다.
하지만 많은 경우에 이런 오버헤드는 람다 표현식을 인라인 방식으로 적용함으로 써 제거될 수 있다.
아래 예제와 같은 경우 lock() 같은 함수는 호출 위치에서 쉽게 인라인(inline) 될 수 있을 것이다.
lock(l) { foo() }
파라미터의 foo() 를 위해 함수 오브젝트를 만들고 호출하는 동작을 만드는 대신에 컴파일라가 아래와 같은 코드로 변경해 주면, 동일한 동작에 대해 오버헤드가 줄어드는 결과를 얻을 수 있을 것이다.
l.lock()
try {
foo()
}
finally {
l.unlock()
}
inline 수정자(modifier) 를 사용하면 컴파일러가 위와 같은 동작을 하도록 할 수 있다.
inline fun <T> lock(lock: Lock, body: () -> T): T { ... }
inline 은 함수 자신과 인자로 전달된 람다에 모두 적용된다. 모두 호출되는 위치에 인라인 될 것이다.
noinline
단지 몇개의 람다만 인라인 함수로 전달하고자 할때는 해당하는 함수 파라미터에만 noinline modifier 를 쓸 수 있다.
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }
인라인 가능한 람다 는 인라인 함수 내에서만 호출 되거나 또는 인라인 가능한 argument 로써만 전달될 수 있다.
하지만 noinline 함수는 다양하게 방식으로 쓰일 수 있다.
인라인 함수가 인라인 가능한 (inlinable) 함수 파라미터가 없거나 reified type parameters 가 아닐때는 컴파일러는 경고를 발생시킬 것이며, 이는 그런 함수를 인라인 함으로써 얻을 수 있는 이득이 매우 적기 때문이다.
그런 경고에도 써야 한다면 @Suppress("NOTHING_TO_INLINE") 표시를 사용할 수 있다.
Non-local returns
람다를 종료하기 위해서는 라벨 을 반드시 써야 하며 람다는 둘러싼 함수(enclosing function) 에서 리턴을 할 수 없기 때문에 return 만 쓰는것은 금지된다.
fun ordinaryFunction(block: () -> Unit) {
println("hi!")
}
fun foo() {
ordinaryFunction {
return // ERROR: cannot make `foo` return here
}
}
람다가 인라인 되기 위해 전달된 경우에는 리턴도 인라인 되기 때문에 return 사용이 가능해 진다.
inline fun inlined(block: () -> Unit) {
println("hi!")
}
fun foo() {
inlined {
return // OK: the lambda is inlined
}
}
람다 안에 있지만 둘러싼 함수에 존재하는 리턴을 non-local return 이라 한다.
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true // returns from hasZeros
}
return false
}
어떤 인라인 함수는 함수 바디에서 바로 파라미터로 전달된 것이 아닌, 로컬 오브젝트(local object) 나 중첩 함수(nested function) 같은 또다른 실행 문맥 으로부터 파라미터로 전달된 람다를 호출할 수도 있다.
이런 경우에는 non-local flow 또한 람다에서는 허용되지 않는다.
이것을 명시하기 위해서 람다 파라미터는 crossinline modifier 로 표시되어야 한다.
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ...
}
break and continue are not yet available in inlined lambdas, but we are planning to support them too.
Reified type parameters
때로는 파라미터로 전달된 타입을 접근할 필요가 있다.
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p.parent
}
@Suppress("UNCHECKED_CAST")
return p as T?
}
이건 tree 를 거슬로 올라가 reflection 을 사용하여 해당 노드가 특정 타입을 가지는지 체크한다.
호출하는 부분이 별로 이쁘지 않다.
treeNode.findParentOfType(MyTreeNode::class.java)
실제로 아래처럼 타입을 함수로 전달하는 방법이 간단할 것이다.
treeNode.findParentOfType<MyTreeNode>()
위의 내용이 reified type parameters 을 지원하는 인라인 함수를 통해 아래처럼 사용될 수 있다.
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
reified modifier 가 사용됨으로써 타입 파라미터 T 는 마치 일반 클래스 처럼 함수 내에서 접근이 가능해 진다.
함수는 인라인 되기 때문에 reflection 은 필요하지 않고, !is, as 같은 일반 오퍼레이터가 동작이 가능해 진다.
또한 myTree.findParentOfType<MyTreeNodeType>() 도 호출이 가능해 진다.
대부분의 경우에는 reflection 이 필요하지 않지만 reified type parameter 와 함께 사용할수는 있다.
inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
인라인이 아닌 일반 함수는 reified parameter 를 가질 수 없다.
run-time representation 을 가지지 않은 type 은 reified type parameter 를 위한 argument 로 쓰일 수 없다.
Inline properties (since 1.1)
inline modifier 는 backing field 를 가지지 않은 프로퍼티의 접근자에 쓰일 수 없다.
각각의 프로퍼티 접근자에는 쓸 수 있다.
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ...
inline set(v) { ... }
접근자 두개를 다 가진 프로퍼티 전체를 위해서는 쓸 수 있다.
inline var bar: Bar
get() = ...
set(v) { ... }
호출되는 위치에서 인라인 접근자는 일반 인라인 함수처럼 처리된다.
Restrictions for public API inline functions
인라인 함수가 public 이거나 protected 이고 private 이나 internal 선언의 일부가 아니면, module 의 public API 로 간주된다.
이것은 다른 모듈에서 호출될 수 있고 호출되는 위치에 인라인 될 수도 있다.
코드 변경 후 재컴파일 되지 않은 모듈을 호출하는 경우, 인라인 함수를 선언하는 그 모듈의 변경사항으로 인해 binary incompatibility(비호환) 의 위험을 가지게 되는 문제가 있다.
위와 같은 문제를 가진 모듈의 non-public API 때문에 public API 인라인 함수는 non-public-API 선언을 사용하는것이 금지된다.
internal 선언은 public API 인라인 함수에서 사용이 가능한 @PublishedApi 과 함께 표시될 수 있다.
internal 인라인 함수가 @PublishedApi 로 표시되면 그 함수의 body 도 public 인 것 처럼 체크된다.
'kotlin' 카테고리의 다른 글
코틀린(Kotlin) Collections : Iterators (0) | 2020.01.02 |
---|---|
코틀린(Kotlin) Collections : Constructing Collections (0) | 2020.01.02 |
코틀린(Kotlin) : 고차함수 와 람다 (Higher-Order Functions and Lambdas) (0) | 2020.01.02 |
코틀린(Kotlin) : 함수(Function) (0) | 2020.01.02 |
코틀린(Kotlin) 클래스(Class) : 위임(Delegation) (0) | 2020.01.02 |