커니의 코틀린이라는 책을 참고하여 코틀린을 공부했다. inline이라는 개념이 나왔지만 간단하게만 설명이 되어있었고, 필자도 간단하게만 정리하고 넘어갔다. 그런데 다시 보니 무슨 개념인지 모르겠어서 정리하려고 한다.

OverView

문서는 다음과 같이 설명되어있다.

고차 함수를 사용하면 런타임 패널티가 있기 때문에 함수 구현 자체를 코드 내부에 넣음으로써 오버헤드를 없앨 수 있다.

무슨 내용인지 잘 와닿지 않는다. 다음의 글을 보고 다시 생각해보자.

일급 함수

  • 일급 함수는 스스로 객체로써 취급되는 함수로 다른 함수를 파라미터로 전달받고 반환할 수 있는 함수를 뜻한다.
  • 코드를 통해서 확인해보자.
1
2
3
4
5
fun print(body: (Int, Int) -> Int){
println(body(5,5))
}

print({a,b -> a})
  • 위 함수는 두 개의 정수를 받아 하나의 정수값만을 출력하는 함수이다.
  • 위와 같은 선언을 하게 된다면 Java에서 아래와 같은 코드로 변화을 하여 사용할 수 있다.
1
2
3
4
5
public final void print(@NotNull Fuction2 body){
Intrinsics.checkParameterIsNotNull(body, "body");
Object result = body.invoke(5,5);
System.out.println(result);
}
  • 위와 같이 자동으로 컨버팅이 될 수 있기 때문에 아래와 같은 유동적인 방법도 사용이 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private fun printResult(body: (Int, Int) -> Int){
println(body(10,5))
}

fun sum(a: Int, b: Int) = a + b
fun subtract(a: Int, b: Int) = a - b

fun main(args:Array<String>){
printResult(::sum)
printResult(::subtract)
}
// 결과
15
5
  • 위의 코드처럼 sum이나 subtract를 유동적으로 붙일 수 있다.

inline

1
2
3
4
5
6
7
8
9
fun doSomething(body: () -> Unit){
body()
}

fun callFunction(){
doSomething{
print("문자열 출력!")
}
}
  • 위의 코드를 자바로 표현하면 다음과 같다.
1
2
3
4
5
6
7
public void doSomething(Function body){
body.invoke();
}

public void callFunction(){
doSomething(System.out.println("문자열 출력!"));
}
  • 위의 코드와 같이 자바로 표현된다. 그리고 이 자바코드는 아래와 같이 변환된다.
1
2
3
4
5
6
7
8
public void callFunction(){
doSomething(new Function(){
@Override
public void invoke(){
System.out.println("문자열 출력!");
}
});
}
  • 문제는 위의 sum이나 subtract처럼 조합하는 함수가 많아질수록 계속 N개만큼의 function 오브젝트가 생성된다. 이럴때 사용하게 되는 것이 inline 키워드이다.
1
2
3
4
5
6
7
8
9
inline fun doSomething(body: () -> Unit){
body()
}

fun callFunction(){
doSomething{
println("문자열 출력")
}
}
  • 위의 코드는 아래와 같이 변환된다.
1
2
3
public void callFunction(){
System.out.println("문자열 출력!");
}
  • 그리고 실제로 컴파일 시 doSomething()의 body()를 호출하는 부분에 저렇게 선언된 함수가 그대로 들어가게 된다.
1
2
// body() 부분에 그대로 들어온다.
System.out.println("문자열 출력!");
  • 위와 같이 Fuction 인스턴스를 만들지 않고 callFunction 내부에 삽입되어 바로 선언되어지게 된다. 때문에 람다함수와 1급 함수가 호출된 곳에서 해당 함수를 가지게 된다.
  • 하지만 inline 함수는 주의할 점이 있는데, private 키워드를 사용하여 함수를 정의할 수 없다. 대신 다른 접근 한정자인 internal을 사용해야 한다.

noinline

  • 모든 람다함수에 inline을 쓰고싶지 않을 수 있다. 이 경우 아래와 같이 해당 람다 함수에 noinline 키워드를 추가해준다.
1
2
3
4
inline fun callLambda(aLambda: () -> Unit, noinline dontInlineLambda: () -> Unit,
aLambda2: () -> Unit){
// 실행.
}
  • 모든 함수를 inline을 사용하여 내부로 컨버팅 되어지길 원치않을 수 있다. 이 경우 위처럼 inline을 먼저 선언한 뒤 람다함수 중 사용하지 않을 함수에 noinline 키워드를 붙여준다.

참고