Kotlin/개념

Kotlin Default Argument 의 동작 원리

TheWing 2023. 1. 1. 01:47

Kotlin Default Argument의 동작 원리

Default Arguments

  • 함수를 호출할 때 명시적으로 인자(Argument) 지정할 필요가 없는 것을 Default Argument라고 한다.

  • 인자를 전달하지 않고 함수를 호출하면 Default Argument가 Function Parameter로 사용된다. 다른 경우에는 함수 호출 중에 argument가 전달되면 전달된 argument가 Function Parameter로 사용된다.

  • Function의 매개 변수는 해당 인자를 건너뛸 때 사용되는 기본 값을 가질 수 있다.

  • 이것은 오버로딩의 수를 줄여준다.

  • ex)

  • fun method(arg1: Int) { 
        /*...*/ 
    } 
    fun method(arg1: Int, arg2: Int) {
        /*...*/ 
    }
  • Default Parameter는 parameter name: type = value 로 아래와 같이 사용이 가능하다.

  • fun method(arg1: Int, arg2: Int = 0) {
       // ...
    }

Default Argument가 사용되는 3가지 경우

Default Argument가 지정된 함수를 호출하는 동안

  1. 인자가 전달되지 않는 경우
  • 함수를 호출하는 동안 인자가 전달되지 않으면 Default Argument가 함수 매개변수로 사용된다. 함수를 정의하는 동안 변수를 초기화해야 한다.
  • fun main() { 
        method() 
    }
    fun method(arg1: Int = 1, arg2: Int = 2) {
        /* ... */ 
        println("arg1 = ${arg1}")
        println("arg2 = ${arg2}")
        println("인자가 전달되지 않는 경우")
    } 
    // 출력 결과 
    arg1 = 1 
    arg2 = 2 
    인자가 전달되지 않는 경우
  1. 부분 인자가 전달되는 경우
  • 여기서 부분 인자는 함수를 호출하는 동안 전달되고 이들은 함수 매개 변수로 사용된다. 함수 호출에서 값을 가져오지 않으면 해당 매개변수에 대해 Default Value가 사용된다.

  • fun main() {
    val arg1 = 3
    method(arg1)
    }
    
    fun method(arg1: Int = 1, arg2: Int = 2) {
        /* ... */
        println("arg1 = ${arg1}")
        println("arg2 = ${arg2}")
        println("부분 인자가 전달 되는 경우")
    }
    
    // 출력 결과
    arg1 = 3
    arg2 = 2
    부분 인자가 전달 되는 경우
  1. 모든 인자가 전달되는 경우
  • 여기서 함수 정의에 정의된 대로 모든 인자를 전달해야 하지만 실제 인자의 데이터 유형은 선언된 인자의 데이터 유형과 순서가 동일해야 한다.

  • fun main() {
        val arg1 = 3
        val arg2 = 4
        method(arg1, arg2)
    }
    
    fun method(arg1: Int = 1, arg2: Int = 2) {
        /* ... */
        println("arg1 = ${arg1}")
        println("arg2 = ${arg2}")
        println("모든 인자가 전달 되는 경우")
    }
    
    // 출력 결과
    arg1 = 3
    arg2 = 4
    모든 인자가 전달 되는 경우

Kotlin Default Argument 동작 원리

위에는 사용법을 설명한 것이고 동작 원리를 알아보도록 하자.

Example Code

fun main() {
    method(
        param3 = "5",
        param4 = "6"
    )
}

fun method(
    param1: String? = "1",
    param2: String? = "2",
    param3: String? = "3",
    param4: String? = "4",
    param5: String = "5",
) {

}

ByteCode

INVOKESTATIC example/DefaultArgumentMethodTestKt.method$default
  • int 값 19를 넣어주고 method명:default로 생성된 static method를 호출하는 부분이 있다.
  • 19는 왜 넣어주는 것인가? decompile 한 코드를 확인해 보자

Decompile Code

main 메서드 보면 default method를 호출할 때 우리가 선언한 5개 인자뿐 아니라 2개의 인자를 더 넣어주고 있다.

method$default_((String)_null_, (String)_null_, "5", "6", (String)_null_, 19, (Object)_null_);

이렇게 넣어주고 있는데 19? 이게 무엇인지 궁금해할 것이다.

method$default를 한번 살펴보자 이게 Default Argument 동작 원리의 핵심이다.

6번째 인자인 int var5 를 가지고 비트 연산을 하여 default value를 대입해주는 것을 확인할 수 있다.

왜 1, 2, 4, 8, 16인가?

  • 첫 번째 인자는 1
  • 두 번째 인자는 2
  • 세 번째 인자는 4
  • 네 번째 인자는 8
  • 다섯 번째 인자는 16
  • 여섯 번째 인자는 32
  • 일곱 번째 인자는 64

이렇게 인자가 하나씩 증가할 때마다 2배씩 늘어난다.

비트 연산으로 default 인자가 들어왔는지 체크하는 로직인데 십진수 19를 2진수로 변환하면 10011 가 나온다.

이걸 비트 연산을 수행하면 3번째 4번째 조건문을 타지 않기 때문에 호출 시 직접 넣어준 value가 대입된다

  • var2 = “3”, var3 = “4”

최종적으로 대입된 value들을 가지고 우리가 생성한 함수( method(var0, var1, var2, var3, var4); )를 호출한다.

+호출 시 null인 인자들을 체크한다.

궁금증 method명$default 메서드를 만들면 어떻게 될까?

Example Code

fun main() {
    DefaultArgumentMethodTest().method(
        param3 = "5",
        param4 = "6"
    )
}

class DefaultArgumentMethodTest {
    fun method(
        param1: String? = "1",
        param2: String? = "2",
        param3: String? = "3",
        param4: String? = "4",
        param5: String = "5",
    ) {

    }

    fun `method$default`(
        var0: DefaultArgumentMethodTest,
        var1: String?,
        var2: String?,
        var3: String?,
        var4: String?,
        var5: String?,
        var6: Int,
        var7: Any?
    ) {
        var var1 = var1
        var var2 = var2
        var var3 = var3
        var var4 = var4
        var var5 = var5
        if (var6 and 1 != 0) {
            var1 = "1"
        }
        if (var6 and 2 != 0) {
            var2 = "2"
        }
        if (var6 and 4 != 0) {
            var3 = "3"
        }
        if (var6 and 8 != 0) {
            var4 = "4"
        }
        if (var6 and 16 != 0) {
            var5 = "5"
        }
        var0.method(var1, var2, var3, var4, var5!!)
    }

}
  • 이렇게 실행 시 에러가 터진다. 이렇게 메서드명을 지을 리가 없지만 하지 않도록 하자!

Reference