首页 > 解决方案 > 在 Kotlin 中实现动态属性分配

问题描述

data class Person private constructor(
   var name: ObservableField<String> = ObservableField("Tony Starks"),
   var age: ObservableField<Int> = ObservableField(40),
   var gender: ObservableField<String> = ObservableField("Male"),
   var test: String = "Tony Starks",
) {
   operator fun set(key: String, value: String) {
       // Tried this approach but it didn't work.
       this[key] = value
   }
}

我想要实现的是这个;

val person = Person()
person['test'] = "James Bond"

我已经能够用 PHP 实现同样的目标

class Person {
    private $name;
    private $age;
    private $gender;
  
    public __get($key) {
      return $this[$key];
    }
  
    public __set($key, $value) {
        $this[$key] = $value;
    }
}

是否有可能在 Kotlin 中实现这一点?

标签: phpkotlindynamic

解决方案


这种行为在像 Kotlin 这样的强类型语言中是一种反模式。通常明智的做法是不要试图强迫强类型语言表现得像弱类型语言。使用强类型语言时,您的整个思维和设计方式应该有所不同。

也就是说,可以通过使您的类成为 MutableMap 并将相关属性委托给自身来获得类似的语法。您可以通过委托(如下所示)或通过继承 HashMap 来使其成为 MutableMap。data class但是,如果您使用的是属性委托,则不能将其设为 a 。

class Person: MutableMap<String, Any?> by mutableMapOf() {
    var test: String by this
    init {
        test = "Tony Stark"
    }

    override fun toString() = "Person(test='$test')"
}

fun main()  {
    val person = Person()
    println(person)
    person["test"] = "Pepper Potts"
    println(person)
    person.test = "Happy Hogan"
    println(person["test"])
}

/** Prints:
Person(test='Tony Stark')
Person(test='Pepper Potts')
Happy Hogan
*/

这样做的缺点(类似于弱类型语言中的问题)是:

  • 如果您在检索属性之前忽略设置属性的初始值,它将崩溃
  • 如果您在属性名称中有拼写错误,它将崩溃
  • 如果您在设置名称时出现拼写错误,它将默默地无法更改所需的属性。

尽管 Kotlin 通常是一种反模式,但 Kotlin 能够将属性委托给 Map 的原因在于,它在处理运行时动态数据(例如 JSON)时很有用,您可能会在类表示之间来回转换。


推荐阅读