Java Developer

Talk is cheap. Show me the code.

Kotlin Part#7

16 Jul 2018

Lambda Function

kotlin은 java처럼 lambda function을 제공합니다.

Function

Java8에 추가된 Function은 input을 하나 받아서 출력으로 하나를 반환합니다. 코드를 작성하기 위해서는 실제 함수와는 다르게 인라인 클래스 처럼 작성을 해야합니다.

Function<String, String> mylambda = s -> {
    System.out.println("Hello," + s);
    return s;
};

String value ="test";

mylambda.apply(value);

apply를 호출해야 인라인의 람다가 실행이 됩니다.

이와 다르게 kotlin의 경우 함수 자체를 변수로 만들수가 있습니다.

val mylambda = { s: String ->
   println("Hello, $s!")
}
mylambda("test")

mylambda라는 함수 자체를 사용하기 위해서는 실제 함수처럼 사용을 하면 됩니다. 비슷하지만, apply를 안쓴다는 점이 틀립니다. 위의 kotlin코드는 .class 파일이 2개가 생성이 됩니다.

HelloworldKt$main$mylambda$1.class 이렇게 람다에 관련된 함수가 클래스로 생성이 되고, 실제 호출하는 클래스는 다음과 같습니다.

Function1 mylambda = (Function1)HelloworldKt.main.mylambda.1.INSTANCE;
mylambda.invoke("test");

mylambda 인스턴스를 받아서 invoke로 호출을 합니다.

이런 방식으로 클래스이기 때문에 다른 함수에 다음처럼 파라미터 전달도 가능합니다. 함수의 파라미터로 인스턴스가 넘어가게 됩니다.

fun main(args: Array<String>) {
    val mylambda = { s: String ->
        println("Hello, $s!")
    }

    val v:String="test"

    myFun(v,mylambda)

}

fun myFun(a :String, action: (String)->Unit) {
    action(a)
}

위의 코드는 mylambda라는 function을 myFun이라는 함수의 파라미터로 전달해서 다시 mylambda가 동작을 하게됩니다. 즉 main -> myFun -> mylambda가 실행이 됩니다.

public final class HelloworldKt {
  public static final void main(@org.jetbrains.annotations.NotNull String[] args) {
    Function1 mylambda = (Function1)HelloworldKt.main.mylambda.1.INSTANCE;

    String v = "test";

    myFun(v, mylambda);
  }

  public static final void myFun(String a, Function1<? super String, kotlin.Unit> action)
  {
    action.invoke(a);
  }
}

디컴파일 코드를 보면 mylambda라는 Function1 인스턴스를 myFun 함수의 파라미터로 전달을 하고, Unitinvoke를 호출해서 인스턴스내에서 정의된 로직인 print가 동작을 하게 됩니다.

return

lambda funtion을 {} 없이 한줄로 표현을 하기 위해서는 다음 방식으로 작성을 해야합니다.

val mylambda1 = { s: String ->
    println("$s!")
}

val mylambda2 :(String)->Unit ={s:String->print(s)}

mylambda1과 mylambda2는 완전히 동일합니다. 다만 Unit이라는 단어가 쓰임을 알수 있습니다. Unit은 void라고 생각을 하면 될거 같습니다. 만약 lambda function이 리턴값을 가진다면 다음처럼 코드를 작성하면 됩니다.

fun main(args: Array<String>) {

    val fun1: (Int,Int)->Int = { a,b -> Math.max(a,b) }
    var result = fun1(1, 2)
    println(result)

}

fun1이라고 된 lambda function은 숫자 a, b를 받아서 Math.max(a, b)의 결과를 리턴을 합니다.

결론

java8과 유사하지만, 조금 더 자유로운게 kotlin의 장점인거 같습니다.

참조

  • https://marcin-chwedczuk.github.io/lambda-expressions-in-kotlin