如何让Kotlin类成员具备动态访问性

问题

对于Kotlin的默认的类对象来说,一个数据类访问成员的方法仍然是传统的点运算符来访问成员变量。
例如以下类定义:

class LegacyUser(){
    var name: String? = null
    var age: Int? = null
}

或者

//数据类
data class LegacyUser(var name: String, var age: Int)

我们访问时仍然只能按域成员访问方式去访问

 val legacyUser = LegacyUser()
 println(legacyUser.name)
 println(legacyUser.age)

而无法类似Groovy或Js那样对一个类对象的属性使用[ ]访问。
JavaScript的效果:

let pack = { 
	name: '' 
}
pack.name = "Name1"
console.log(pack.name)
console.log(pack['name'])

pack['name'] = 'Name2'
console.log(pack.name)
console.log(pack['name'])

输出:

Name1 
Name1 
Name2 
Name2

而在Groovy当中是如下代码和效果:

class MyPack {
    public def name;
}

def pack = new MyPack()
pack.name = "Name1"
println(pack.name)
println(pack['name'])
pack.name = "Name2"
println(pack.name)
println(pack['name'])

输出:

Name1 
Name1 
Name2 
Name2

以上语言自身的效果不能直接在kotlin中的类直接体现。

如果说我们在开发Android或者后端应用的时候我们有时候需要动态获取成员时,如何避免编写大量的反射代码来实现呢?

实现

Kotlin语言本身并没有设计这种语法,那我们是不是就没有办法了呢?答案是否定的。我们借助Kotlin自身的两个语法特性来实现这个效果。

  • 委托——属性委托
  • 操作符重载

借助这两个语法特性可以来实现我们的效果。

首先我们需要一个数据类,这个类需要一个在构造方法中成员map,另外编写数据字段,并且将这些字段委托给map。我们需要对变量进行读写功能,并且将读写的数据委托给map,需要在构造方法中使用MutableMap类型的映射。
当然了,MutableMap也可以使用HashMap来代替。

class MutableUser() {
    val map: MutableMap<String, Any?> = mutableMapOf("name" to "", "age" to 0)
    var name: String by map
    var age: Int by map
}

现在这个类只有委托数据保存的位置,并没有增加[ ]的访问性。
我们增加两个操作符重载方法来重载[ ]的读写功能:

 operator fun get(value: String): Any? {
     return map[value]
 }
 operator fun set(key: String, value: String) {
     map[key] = value
 }

我们针对操作符进行了重载使得当前对象使用[ ]时可以直接访问map当中的指定key的值。由此我们实现了上述特性的大部分功能。

在编写这个类的时候,要注意map在初始化为

val map: MutableMap<String, Any?> = mutableMapOf("name" to "", "age" to 0)

的时候,mutableMapOf一定要传入与变量名相同的key,并且传入任意一定的数据,比如""0,作为给key占位的数据。否则会在委托变量读写时发生错误。

我们编写如下类:

class MutableUser {
    val map: MutableMap<String, Any?> = mutableMapOf("name" to "", "age" to 0)
    var name: String by map
    var age: Int by map

    operator fun get(value: String): Any? {
        return map[value]
    }

    operator fun set(key: String, value: String) {
        map[key] = value
    }
}

运行测试:

 val mUser = MutableUser()
 mUser.name = "Name1"
 println(mUser.name)
 println(mUser["name"])

 mUser.name = "Name2"
 println(mUser.name)
 println(mUser["name"])

运行结果:

Name1
Name1
Name2
Name2

由此我们在kotlin中实现了通过委托和运算符重载实现了动态访问和修改类成员。

我们将上面的代码重新组织,以便于增加重用性和可读性。

open class Dynamic {
    val dynamic: MutableMap<String, Any?> = mutableMapOf()

    operator fun get(value: String): Any? {
        return dynamic[value]
    }

    operator fun set(key: String, value: Any?) {
        dynamic[key] = value
    }
}

class MutableUser : Dynamic() {
    var name: String by dynamic
}

如此将动态map作为父类来继承,数据字段作为派生类来定义,并且每个数据字段都委托给父类的map,我们就可以实现类似JavaScript和Groovy的动态字段访问的效果了。
但是一定要注意一点,在初始化数据类之后,切不可忘记为每个字段初始化一个值,否则直接访问则会报错。

测试代码:

fun main(args: Array<String>) {
    val mUser = MutableUser()

    mUser.name = "Name1"
    println(mUser.name)
    println(mUser["name"])

    mUser["name"] = "Name2"
    println(mUser.name)
    println(mUser["name"])
}

结果:

Name1
Name1
Name2
Name2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值