Kotlin反射调用getter/setter

11

我是Kotlin的初学者。

我尝试在程序中使用反射创建和填充对象。我找不到Kotlin纯粹相当的功能,所以我的解决方案类似于下面的代码,这可以正常工作,但需要使用类似于java.lang.String::class.java的脏引用,而且IntelliJ不太喜欢它。是否有更简单的方法让我做到这一点?

val jclass = myObject::class.java 
val setters = jclass.declaredMethods.filter { it.name.startsWith("set") }
for (s in setters) {
    val paramType = s.parameterTypes.first()
    val data = when(paramType) {
        java.lang.Integer::class.java -> foo
        java.lang.Double::class.java -> bar
        java.lang.String::class.java -> baz
    }
    s.invoke(myObject, data)
}
2个回答

18

您可以使用Kotlin反射,这需要您将kotlin-reflect添加为项目的依赖项。

在这里,您可以找到适用于Kotlin 1.0.5的kotlin-reflect ,或者如果您使用不同的Kotlin版本,可以选择其他版本

之后,您可以按以下方式重写代码:

val properties = myObject.javaClass.kotlin.memberProperties
for (p in properties.filterIsInstance<KMutableProperty<*>>()) {
    val data = when (p.returnType.javaType) {
        Int::class.javaPrimitiveType,
        Int::class.javaObjectType -> foo
        Double::class.javaPrimitiveType,
        Double::class.javaObjectType -> bar
        String::class.java -> baz
        else -> null
    }
    if (data != null)
        p.setter.call(myObject, data)
}

一些细节:

  • 尽管使用了Kotlin反射,但这种方法也适用于Java类,它们的字段和访问器将被视为属性,如此处所述。

  • 与Java反射一样,memberProperties返回该类型及其所有超类型的public属性。要获取在类型中声明的所有属性(包括private的属性,但不包括来自超类型的属性),请改用declaredMemberProperties

  • .filterIsInstance<KMutableProperty<*>>仅返回可变属性,以便稍后可以使用它们的p.setter。如果需要遍历所有属性的getter,请将其删除。

  • when块中,我将p.returnType.javaTypeInt::class.javaPrimitiveTypeInt::class.javaObjectType进行比较,因为在Kotlin中,Int可以根据其用法映射到Java intjava.lang.Integer。在Kotlin 1.1中,只需检查p.returnType.classifier == Int::class即可。


1
如果您需要获取属性的getter/setter,有几种内置的构造方式可以使用 YourClass::propertyName。请看下面的示例。
fun main(args: Array<String>) {
        val myObject = Cat("Tom", 3, 35)
        println(Cat::age.getter.call(myObject)) // will print 3
        Cat::age.setter.call(myObject, 45)
        print(myObject) // will print Cat(name=Tom, age=45, height=35)
    }

    data class Cat(var name : String, var age : Int, val height : Int)

有时您可能不知道类的确切信息(使用泛型),或需要获取属性列表,则可以使用val <T:Any> KClass<T>.declaredMemberProperties:Collection<KProperty1<T,*>>,它将返回所有属性,其中一些可以是可变的(var),一些是不可变的(val),您可以通过检查是否属于KMutableProperty<*>(通过使用is运算符进行过滤或使用方便的方法,例如filterIsInstance<KMutableProperty<*>>)来找出不可变性。

关于您的代码片段

我完全同意hotkey的观点,但现在最好使用myObject::class.declaredMemberProperties而不是myObject.javaClass.kotlin.memberProperties

因为第二个已被弃用

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html

     data class Cat(var name : String, var age : Int, val height : Int)

     @JvmStatic
            fun main(args: Array<String>) {
                val myObject = Cat("Tom", 3, 35)
                val properties = myObject::class.declaredMemberProperties
                for (p in properties.filterIsInstance<KMutableProperty<*>>()) {
                    val data = when (p.returnType.javaType) {
                        Int::class.javaPrimitiveType,
                        Int::class.javaObjectType -> 5
                        String::class.java -> "Rob"
                        else -> null
                    }
                    if (data != null)
                        p.setter.call(myObject, data)
                }
                println(myObject)
                // it will print Cat(name=Rob, age=5, height=35),
                // because height isn't var(immutable)
            }

一般来说,我会考虑这种构造方式来解决类似的问题。
val myObject = Cat("Tom", 3, 35)

Cat::class.declaredMemberProperties
                    //if we want only public ones
                    .filter{ it.visibility == KVisibility.PUBLIC }
                    // We only want strings
                    .filter{ it.returnType.isSubtypeOf(String::class.starProjectedType) }
                    .filterIsInstance<KMutableProperty<*>>()
                    .forEach { prop ->
                        prop.setter.call(myObject, "Rob")
                    }

println(myObject)
//it will print Cat(name=Rob, age=3, height=35),
//because name is only eligible in this case

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接