2017-10-20 2 views
1

Java에서 Kotlin으로 클래스를 이식하고 있습니다. 이 클래스는 수백 개의 객체를 선언합니다. 각 객체에는 객체의 선언 된 변수 이름과 동일한 name 속성이 있습니다. Java reflection을 사용하면 리플렉션을 통해 선언 된 이름을 사용하여 객체 멤버 name을 설정할 수 있습니다. 하나의 매개 변수를 수백 개의 생성자에 저장합니다.Kotlin 리플렉션을 사용하여 멤버 필드를 변경하는 방법은 무엇입니까?

나는 Kotlin에서 동일한 작업을 시도하지만 속성 설정을 수행하는 방법을 알아낼 수 없습니다.

package myPackage 

import kotlin.reflect.full.companionObject 
import kotlin.reflect.full.declaredMemberProperties 

class MyTestObject() { 

    var name: String = "NotInitialized" 

    companion object { 
    val Anton = MyTestObject() 
    val Berta = MyTestObject() 
    val Caesar = MyTestObject() 
    } 
} 

fun main(args : Array<String>) { 
    println(MyTestObject.Anton.name) // name not yet initialized 

    // Initialize 'name' with the variable name of the object: 
    for (member in MyTestObject::class.companionObject!!.declaredMemberProperties) { 
    if (member.returnType.toString() == "myPackage.MyTestObject") { 
     println("$member: ${member.name}") 

     // Set 'name' property to 'member.name': 
     // ??? 
    } 
    } 

    println(MyTestObject.Anton.name) // now with the initialized name 
} 

??? 라인은 내가 member.name에로 설정하는 MyTestObjectname 재산에 접근하고 싶은 곳이다 : 다음은 몇 가지 간단한 테스트 코드입니다. (member.toObject() as MyTestObject).name = member.name과 비슷한 기능을 찾고 있습니다.

답변

2

kotlin-reflection은 유형 안전을 위해 노력하지만 유형 시스템과 추론 논리는 유형 안전 방법으로 수행하려고하는 것과 같은 것을 허용하기에 충분하지 않을 때도 있습니다. 따라서 유형에 대한 지식이 컴파일러가 추론 할 수있는 것 이상임을 명시하는 체크되지 않은 캐스트를 만들어야합니다. 당신이 대체 할 수있는 경우

@Suppress("UNCHECKED_CAST") 
(member as KProperty1<Any, MyTestObject>) 
    .get(MyTestObject::class.companionObject!!.objectInstance!!) 
    .name = member.name 

: 당신은 .get(...)에 동반자 개체 인스턴스를 통과하고 MyTestObject으로 결과를 사용으로 // ??? 라인을 대체 할 수 있도록 경우

, 그것은 member 캐스팅 충분 MyTestObject.Companion::classMyTestObject::class.companionObject!!는 체크되지 않은 캐스트가 필요하지 않은 (즉, 실제 사용 사례는 다른 클래스에서 .companionObject을 받고 포함하지 않는다), 그리고이와 위의 문을 대체 할 수

(member.get(MyTestObject.Companion) as MyTestObject).name = member.name 

동반 개체 리플렉션을 전혀 필요로하지 않는 대안으로 위임을 사용하여 동일한 바인딩 논리를 수행 할 수 있습니다. Implementing provideDelegate는 속성을 초기화하는 논리를 사용자 정의 할 수 있습니다, 당신은 이름을 할당 할 수있는 곳이다 : 여기에

val Anton by MyTestObject() 
val Berta by MyTestObject() 
val Caesar by MyTestObject() 
+0

첫 번째 예가 효과가 있습니다. 두 번째 동일한 클래스 MyTestObject 시도했지만 get() 항상 null을 제공합니다. 이 접근법에 익숙하지 않아 소프트웨어를 지나치게 복잡하고 읽을 수 없게 만드는 경향이 있으므로 위임 예제를 시도하지 않았습니다. –

+0

두 번째 예제는 속성 선언 다음에 동반자 개체의 코드를 초기화 코드로 옮기면 작동합니다. 이것은 간단하고 읽기 쉬운 솔루션처럼 보입니다. 이 솔루션을 제공해 주셔서 감사합니다! –

0

로 속성을 선언

operator fun MyTestObject.provideDelegate(
    thisRef: MyTestObject.Companion, 
    property: KProperty<*> 
) = apply { name = property.name } 

operator fun MyTestObject.getValue(
    thisRef: MyTestObject.Companion, 
    property: KProperty<*> 
) = this 

을 최종 테스트 코드 단축키의 솔루션을 기반으로 :

package myPackage 

import kotlin.reflect.full.declaredMemberProperties 

class MyTestObject() { 

    lateinit var name: String 

    companion object { 
    val Anton = MyTestObject() 
    val Berta = MyTestObject() 
    val Caesar = MyTestObject() 

    init { 
     for (member in MyTestObject.Companion::class.declaredMemberProperties) { 
     if (member.returnType.toString() == "myPackage.MyTestObject") { 
      (member.get(MyTestObject.Companion) as MyTestObject).name = member.name 
     } 
     } 
    } 
    } 
} 

fun main(args : Array<String>) { 
    println(MyTestObject.Anton.name) 
    println(MyTestObject.Caesar.name) 
} 
관련 문제