Kotlin代理之属性代理

属性代理

属性代理实际就是将属性的值的设置(set)和获取(get)的流程交给了其他的对象代理,相当于为原对象增加了一个backing field(理解为存储值的内存),变量的实际值一般被保存在代理对象中;

var与val设置代理

对于var变量,要求代理必须实现getValue和setValue操作符方法,而val只需要实现getValue;

比如常用的lazy代理,只能用在val常量上:

    val lazyStr :String by lazy{
        "lazyStr"
    }

只能用来代理val常量,var变量则不行,因为其缺少setValue方法;

以下的代理,在val和var上都可以用:

    //在被使用前,如果没有先赋值,则会报IllegalStateException异常
    var nnv by Delegates.notNull<String>()

    //监测一个属性,如果其被进行赋值操作,则会回调下面的字面函数,打印log
    var obserV :Int by Delegates.observable(0){_,old, new ->
        println("obserV : $old -> $new")
    }

    //在属性要被进行赋值操作之前,回调下面的字面函数,如果返回FALSE,则属性不会被赋值
    var vetoableV:Int by Delegates.vetoable(0){_,old, new ->
        println("obserV : $old -> $new")
        true
    }

    //Delegate的observable和vetoable其实都是实现ObservableProperty,我们可以自己实现,两个
    //方法一起用
    var ovV : Int by object : ObservableProperty<Int>(0) {
        override fun beforeChange(property: KProperty<*>, oldValue: Int, newValue: Int): Boolean {
            return super.beforeChange(property, oldValue, newValue)
        }

        override fun afterChange(property: KProperty<*>, oldValue: Int, newValue: Int) {
            super.afterChange(property, oldValue, newValue)
        }
    }

使用Map、MutableMap做代理

查看MapAccessors.kt可以发现,其中扩展了Map的getValue和MutableMap的getValue、setValue方法:

@kotlin.internal.InlineOnly
public inline operator fun <V, V1: V> Map<in String, @Exact V>.getValue(thisRef: Any?, property: KProperty<*>): V1
        = @Suppress("UNCHECKED_CAST") (getOrImplicitDefault(property.name) as V1)

@kotlin.jvm.JvmName("getVar")
@kotlin.internal.InlineOnly
public inline operator fun <V> MutableMap<in String, in V>.getValue(thisRef: Any?, property: KProperty<*>): V
        = @Suppress("UNCHECKED_CAST") (getOrImplicitDefault(property.name) as V)

@kotlin.internal.InlineOnly
public inline operator fun <V> MutableMap<in String, in V>.setValue(thisRef: Any?, property: KProperty<*>, value: V) {
    this.put(property.name, value)
}

那么可以用Map来做代理,存储值:

    //定义不可变的Map,只有getValue
    val map = mapOf<String, String>("cat" to "fish", "monkey" to "banana")
    //将属性交给map代理
    val cat by map
    //调用getValue方法,map会根据属性的名字"cat"找到对应的value "fish"
    print(cat)

用可变的Map,向其中put键值对时,会先找是否有相同属性名的key,有则将值赋给这个属性:

    val relMap = HashMap<String, String?>()
    //交给relMap代理,如果向relMap中设置key为"first"、"second"的键值对,则
    //会自动将值赋给以下两个属性;
    val first by relMap
    val second by relMap

    relMap.put("first", "first value")
    relMap.put("second", "second value")

    println(first)
    println(second)

    //输出:
    //first value
    //second value

自定义代理

既然实现了getValue和setValue操作符就可以作为代理,那么我们也可以自定义代理;

可以直接实现这两个方法,也可以通过实现ReadOnlyProperty和ReadWriteProperty接口:

public interface ReadOnlyProperty<in R, out T> {
    /**
     * Returns the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @return the property value.
     */
    public operator fun getValue(thisRef: R, property: KProperty<*>): T
}

public interface ReadWriteProperty<in R, T> {
    /**
     * Returns the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @return the property value.
     */
    public operator fun getValue(thisRef: R, property: KProperty<*>): T

    /**
     * Sets the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @param value the value to set.
     */
    public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

此处就直接实现这两个方法:

//定义一个叫X的代理类
class X{
    var value: String? = null
    //实现getValue操作符
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return value?:"hello"
    }
    //实现setValue操作符
    operator fun setValue(thisRef: Any?, property: KProperty<*>,value:String){
        this.value = value
    }
}

    val x: String by X()
    var x2: String by X()

    println(x)
    println(x2)

    //输出
    //hello x
    //hello x2

自定义File代理

属性代理让变量的设置和赋值过程能做更多的事,将变量的获取和设置流程封装到代理对象中,这样我们在使用对象的设置和赋值过程就能更加简洁,比如文件的读取和设值,直接通过访问变量就可以完成;

var file: File by FileDelegate()

class FileDelegate{
    var file: File? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): File {
        if (file == null) {
            file = File("/root")
        }
        //在这里可以为file属性做更多的初始化条件
        return file!!
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: File){
        //初始化...
        //设置更多的条件
        this.file = value
    }
}

使用:

    println(file.path)
    file = File("/root/help")
    println(file.path)

自定义SharedPreference 的代理

class Preference<T>(val context: Context, val name: String, val default: T, val prefName: String = "default") : ReadWriteProperty<Any?, T> {
    constructor(context: Context, default: T, prefName: String = "default"): this(context, "", default, prefName)
    val prefs by lazy { context.getSharedPreferences(prefName, Context.MODE_PRIVATE) }
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return findPreference(findProperName(property), default)
    }
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        putPreference(findProperName(property), value)
    }
    private fun findProperName(property: KProperty<*>) = if(name.isEmpty()) property.name else name
    private fun <U> findPreference(name: String, default: U): U = with(prefs) {
        val res: Any = when (default) {
            is Long -> getLong(name, default)
            is String -> getString(name, default)
            is Int -> getInt(name, default)
            is Boolean -> getBoolean(name, default)
            is Float -> getFloat(name, default)
            else -> throw IllegalArgumentException("Unsupported type")
        }
        res as U
    }
    private fun <U> putPreference(name: String, value: U) = with(prefs.edit()) {
        when (value) {
            is Long -> putLong(name, value)
            is String -> putString(name, value)
            is Int -> putInt(name, value)
            is Boolean -> putBoolean(name, value)
            is Float -> putFloat(name, value)
            else -> throw IllegalArgumentException("Unsupported type")
        }.apply()
    }
}

inline fun <reified R, T> R.pref(default: T) = Preference(AppContext, default, R::class.jvmName)
object Settings {
    var lastPage by pref(0)
}

(参考:用 Map 为你的属性做代理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值