Kotlin代理之属性代理

来源:互联网 发布:2016mac flash安装不了 编辑:程序博客网 时间:2024/05/23 15:05

属性代理

属性代理实际就是将属性的值的设置(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.InlineOnlypublic 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.InlineOnlypublic 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.InlineOnlypublic 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 为你的属性做代理)

原创粉丝点击