設定類的所有屬性的值

作為示例,我們要設定示例類的所有字串屬性

class TestClass {
    val readOnlyProperty: String
        get() = "Read only!"

    var readWriteString = "asd"
    var readWriteInt = 23

    var readWriteBackedStringProperty: String = ""
        get() = field + '5'
        set(value) { field = value + '5' }

    var readWriteBackedIntProperty: Int = 0
        get() = field + 1
        set(value) { field = value - 1 }

    var delegatedProperty: Int by TestDelegate()

    private var privateProperty = "This should be private"

    private class TestDelegate {
        private var backingField = 3

        operator fun getValue(thisRef: Any?, prop: KProperty<*>): Int {
            return backingField
        }

        operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: Int) {
            backingField += value
        }
    }
}

獲取可變屬性建立在獲取所有屬性的基礎上,按型別過濾可變屬性。我們還需要檢查可見性,因為讀取私有屬性會導致執行時異常。

val instance = TestClass()
TestClass::class.memberProperties
        .filter{ prop.visibility == KVisibility.PUBLIC }
        .filterIsInstance<KMutableProperty<*>>()
        .forEach { prop ->
            System.out.println("${prop.name} -> ${prop.get(instance)")
        }

要將所有 String 屬性設定為 Our Value,我們還可以按返回型別進行過濾。由於 Kotlin 基於 Java VM,因此 Type Erasure 生效,因此返回 List<String> 等泛型型別的 Properties 將與 List<Any> 相同。可悲的是,反射不是一個金色的子彈,沒有明智的方法來避免這種情況,所以你需要注意你的用例。

val instance = TestClass()
TestClass::class.memberProperties
        .filter{ prop.visibility == KVisibility.PUBLIC }
        // We only want strings
        .filter{ it.returnType.isSubtypeOf(String::class.starProjectedType) }
        .filterIsInstance<KMutableProperty<*>>()
        .forEach { prop ->
            // Instead of printing the property we set it to some value
            prop.setter.call(instance, "Our Value")
        }