Kotlin反思地调用getter / setter

时间:2016-12-17 21:46:58

标签: reflection kotlin

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 个答案:

答案 0 :(得分:10)

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

如果您使用不同的Kotlin版本,可以在此处找到kotlin-reflect for Kotlin 1.0.5pick another version

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

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类,它们的字段和访问器将被视为属性,如here所述。

  • 与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 :(得分:0)

如果需要获取属性获取器/设置器,则可以使用它的一些内置构造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<*>>之类的便捷方法)来找出不变性

关于您的代码段

我绝对同意热键,但是现在最好使用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