ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Kotlin - Reflection 과 생성자
    Kotiln 2021. 8. 4. 15:56

    Reflection 을 이용하여 인스턴스를 생성하는 방법을 알아봅시다.

    간단한 Person data class 를 생성했습니다.

    data class Person(
        var name: String,
        var age: Number = 20
    )

    age 프로퍼티에 기본 값으로 20 을 주었습니다.

    println("생성자 조회")
    println(Person::class.constructors)
    
    생성자 조회
    [fun <init>(kotlin.String, kotlin.Number): chung.study.Person]

    먼저 생성자를 조회해보겠습니다.

    Person 클래스를 생성한 것 과 같이 파라미터가 2개인 생성자가 조회됐습니다.

    생성자는 KFunction 인터페이스이며 KCallable 인터페이스를 상속받고 있습니다.

    KCallable 인터페이스 안에는 call, callBy 메소드가 있습니다.

    두 메소드를 이용하여 Person 객체를 생성해보겠습니다.

    call 을 이용한 객체 생성

    println("생성자 조회")
    println(Person::class.constructors)
    
    println("call 을 이용한 생성")
    Person::class.primaryConstructor?.call("홍길동", 30)?.let {
        println(it)
    }

    call 메소드에 매개변수를 넣고 결과를 출력해보겠습니다.

    call 을 이용한 생성
    Person(name=홍길동, age=30)

    예상한 결과대로 name=홍길동, age=30 인 객체가 생성되었습니다.

    Person 클래스를 생성할 때 age 에 기본 값을 지정했기 때문에 age 에 들어갈 매개변수를 지우고 생성해보겠습니다.

    println("call 을 이용한 생성 (기본값)")
    Person::class.primaryConstructor?.call("홍길동")?.let {
            println(it)
    }
    java.lang.IllegalArgumentException: Callable expects 2 arguments, but 1 were provided.
    
        at kotlin.reflect.jvm.internal.calls.Caller$DefaultImpls.checkArguments(Caller.kt:20)
        at kotlin.reflect.jvm.internal.calls.CallerImpl.checkArguments(CallerImpl.kt:15)
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Constructor.call(CallerImpl.kt:40)
        at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)

    생성하지 못하고 2개의 매개변수를 예상했지만 한개만 제공되었다는 에러를 볼 수 있습니다.

    기본 값을 이용하려면 callBy 메소드를 사용해야 합니다.

    callBy 메소드는 매개변수로 Map<KParameter, Any?> 를 받습니다.

    val nameKParameter = Person::class.primaryConstructor?.findParameterByName("name")
    val ageKParameter = Person::class.primaryConstructor?.findParameterByName("age")
    if (nameKParameter != null && ageKParameter != null) {
        println("callBy 를 이용한 생성")
        Person::class.primaryConstructor?.callBy(mapOf(nameKParameter to "홍길동", ageKParameter to 30))?.let {
            println(it)
        }
        println("callBy 를 이용한 생성 (순서 상관 없다)")
        Person::class.primaryConstructor?.callBy(mapOf(ageKParameter to 30, nameKParameter to "홍길동"))?.let {
            println(it)
        }
        println("callBy 를 이용한 생성 (기본값)")
        Person::class.primaryConstructor?.callBy(mapOf(nameKParameter to "홍길동"))?.let {
            println(it)
        }
    }

    생성자에서 findParameterByName 메소드를 사용하여 name 과 age 에 해당하는 KParameter 를 가져 온 뒤 각각 값을 넣고 callBy 메소드의 매개변수로 넣어주었습니다.

    callBy 를 이용한 생성
    Person(name=홍길동, age=30)
    callBy 를 이용한 생성 (순서 상관 없다)
    Person(name=홍길동, age=30)
    callBy 를 이용한 생성 (기본값)
    Person(name=홍길동, age=20)

    기본 값이 들어간 객체가 생성되었습니다.

    callBy 메소드는 매개변수와 파라미터( 코드에서 매개변수로 전달한 Map) 를 매핑하여 결과를 반환하는 데 만약 파라미터에 매핑된 값이 없으면 기본 값을 사용합니다.

    Person::class.primaryConstructor?.callBy(mapOf(ageKParameter to 30))?.let {
        println(it)
    }

    기본 값이 없는 파라미터를 넣어주지 않으면 다음과 같이 에러가 발생합니다.

    java.lang.IllegalArgumentException: No argument provided for a required parameter: parameter #0 name of fun <init>(kotlin.String, kotlin.Number): chung.study.Person

    blog-source/kotlin-constructor-reflection at master · sinna94/blog-source

    반응형

    댓글

Designed by Tistory.