ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ Kotlin ] 익명 함수( Anonymous Functions )와 인라인 함수( Inline Functions )
    Kotlin 2020. 8. 4. 16:36

     

     

     익명 함수 ( Anonymous Functions )

    익명함수란 일반 함수이지만 이름이 없는 함수입니다. 람다식과 구성이 비슷하지만 익명 함수의 경우 return, break, continue가 사용 가능하고 람다식에서는 사용하기 어렵습니다. 따라서 조건식에 따라서 함수를 중단하거나 반환해야 하는 경우 일반 익명 함수로 사용해야 할 필요가 있습니다.

     

     

     

     인라인 함수 ( Inline Functions )

    inline함수는 함수가 호출되는 곳에 코드를 복사해 넣어 함수의 분기 없이 처리됩니다. 코드의 복사로 인해 분기가 없다는 점에서 속도를 높일 수 있지만 복사할 코드가 긴 경우 프로그램의 용량도 늘어나고 속도 향상도 되지 않을 수 있습니다. 따라서 inline 함수는 대게 짧은 코드로 작성되고 람다식 매개변수를 가지고 있는 함수 형태를 권장합니다.

    inline을 사용하지 않은 경우와 사용한 경우

    아래의 코드는 inline 함수를 사용하지 않은 경우 Kotlin 코드를 Java로 Decomplie한 것입니다.

    public final class DatatypeKt {
       public static final void shortFunc() {
          String var0 = "Hello";
          boolean var1 = false;
          System.out.println(var0);
       }
    
       public static final void main() {
          shortFunc();
       }
    
       // $FF: synthetic method
       public static void main(String[] var0) {
          main();
       }
    }

    위와 달리 아래의 코드는 inline 함수를 사용하지 않은 경우 Kotlin 코드를 Java로 Decomplie한 것으로 코드 자체를 복사해서 들고 있음을 볼 수 있습니다. 

    public final class DatatypeKt {
       public static final void shortFunc() {
          int $i$f$shortFunc = 0;
          String var1 = "Hello";
          boolean var2 = false;
          System.out.println(var1);
       }
    
       public static final void main() {
          int $i$f$shortFunc = false;
          String var1 = "Hello";
          boolean var2 = false;
          System.out.println(var1);
       }
    
       // $FF: synthetic method
       public static void main(String[] var0) {
          main();
       }

     

    inline 함수 사용의 예

    noinline

    람다식 함수를 매개변수로 가진 함수가 inline으로 정의되어 있더라도 noinline이 있는 람다식 함수는 inline으로 처리되지 않고 분기하여 호출됩니다.

    inline fun sub(out1: () -> Unit, noinline out2: () -> Unit) {
    	...
    }

    corssinline

    Kotlin에서는 익명 함수를 종료하기 위해 ( 반환 값이 없는 ) return 함수를 사용할 수 있습니다.

    람다식 함수를 인자로 사용하는 함수는 의도하지 않게 람다식 함수 바깥에 있는 함수가 같이 반환되어 버리는데 이를 비지역 반환(non-local return)이라고 합니다. 이를 방지하기 위해서 crossinline을 사용할 수 있습니다.

     

    비지역 반환이 일어난 경우

    crossinline 키워드를 사용하면 함수 내에서 return이 사용되는 것을 금지할 수 있습니다.

    crossinline으로 비지역 반환을 막은 경우

     

     확장 함수 ( Extension Function )

    Kotlin에서는 클래스의 기존 멤버 메서드는 아니지만 내가 원하는 함수를 포함시켜 확장할 수 있습니다. ( 클래스 상속이나 디자인 패턴을 사용하지 않고도 새로운 기능을 가지는 클래스로 확장할 수 있습니다. )

     

    Extensions는 실제 클래스를 수정하여 클래스에 새로운 멤버를 추가하는 것이 아니라 Extensions를 정의한 타입의 변수에 .(dot)를 이용하여 새로운 함수를 호출할 수 있게하는 것입니다.

    Kotlin 공식 문서를 살펴보면, 확장 함수가 정적으로(statically) 처리된다는 것을 강조하고 있습니다. 즉, 호출되는 확장 함수가 런타임에 해당 표현식의 type이 결정되는 것이 아니라 함수가 호출될 때 표현식의 type에 의해 결정됩니다.

    fun main() {
        printClassName(Rectangle())
    }
    
    open class Shape
    
    class Rectangle: Shape()
    
    fun Shape.getName() = "Shape"
    
    fun Rectangle.getName() = "Rectangle"
    
    fun printClassName(s: Shape) {
        println(s.getName())
    }

    위의 코드에서 확장 함수는 Shape class의 매개변수 s에 의존하기 때문에 "Shape"를 print합니다. 

    아래는 위의 코드를 디컴파일 했을 경우 코드입니다.

    public final class DatatypeKt {
       public static final void main() {
          printClassName((Shape)(new Rectangle()));
       }
    
       public static void main(String[] var0) {
          main();
       }
    
       @NotNull
       public static final String getName(@NotNull Shape $this$getName) {
          Intrinsics.checkParameterIsNotNull($this$getName, "$this$getName");
          return "Shape";
       }
    
       @NotNull
       public static final String getName(@NotNull Rectangle $this$getName) {
          Intrinsics.checkParameterIsNotNull($this$getName, "$this$getName");
          return "Rectangle";
       }
    
       public static final void printClassName(@NotNull Shape s) {
          Intrinsics.checkParameterIsNotNull(s, "s");
          String var1 = getName(s);
          boolean var2 = false;
          System.out.println(var1);
       }
    }
    
    // Shape.java
    public class Shape {
    }
    
    // Rectangle.java
    public final class Rectangle extends Shape {
    }
    

    확장 함수를 만들 때 만약 확장하려는 대상에 동일한 이름의 멤버 함수/메서드가 존재한다면 확장 함수보다 기존의 멤버 메서드를 우선으로 호출합니다. 그러나 확장 함수가 멤버 함수를 overload한 것이라면 확장 함수를 호출합니다.

    fun main() {
        Example().printFunctionType() // "Class method" print
    }
    
    class Example {
        fun printFunctionType() { println("Class method") }
    }
    
    fun Example.printFunctionType() { println("Extension function") }
    
    fun main() {
        Example().printFunctionType(1) // "Extension function" print
    }
    
    class Example {
        fun printFunctionType() { println("Class method") }
    }
    
    fun Example.printFunctionType(i: Int) { println("Extension function") }

     

     

     

     

    출처

    'Kotlin' 카테고리의 다른 글

    [ Kotlin ] 조건문 / 반복문  (0) 2020.12.04
    [ Kotlin ] 중위함수와 꼬리재귀 함수  (0) 2020.12.03
    [ Kotlin ] 람다식 ( Lambda )  (0) 2020.08.04
    [ Kotlin ] 함수형 프로그래밍  (0) 2020.08.03
    [ Kotlin ] 함수  (0) 2020.08.03

    댓글

Designed by Tistory.