在上一次https://www.cnblogs.com/webor2006/p/11369019.html中学习了类委托,这次来学习一下属性的委托(delegated property),我们知道定义一个类的属性是需要给它一个初始值的,如果不给会报错,如下:
![](https://img-blog.csdnimg.cn/img_convert/9abb3a57190e7c971b88545b3ebcd772.png)
当然啦,可以加一个延迟属性来避免:
![](https://img-blog.csdnimg.cn/img_convert/04931fcd02da691b04346460c7f16e1d.png)
当然咱们不用这种方式,而是可以将此属性的赋值进行委托,目前该属性是可读可写,则在委托属性中需要定义可读可写的,而如果是用的val定义的属性则只需要定义可读的委托属性,具体如何做呢?下面先来定义一个委托类:
![](https://img-blog.csdnimg.cn/img_convert/a548909e4b456e154604f1060336140d.png)
接下来需定义两个读写方法,注意:该方法的名称是有严格要求的,不能乱写方法名,下面来定义一下:
![](https://img-blog.csdnimg.cn/img_convert/31a17e71e0672529b9d6e7b90fa685d7.png)
接下来则需要给它定义参数,第一个参数是要给哪个类进行委托,第二个参数是其委托的属性是哪一个,定义如下:
![](https://img-blog.csdnimg.cn/img_convert/065ceb4768c2ae6d0e5d76e856235655.png)
接下来再来定义setValue()方法,此时因为需要设置值,所以多了一个参数,其函数的参数大体跟getValue()差不多:
![](https://img-blog.csdnimg.cn/img_convert/6f6e0c8c19d1c4d275b693f91affc44e.png)
接下来则将这个委托关联到我们的属性上,如下:
![](https://img-blog.csdnimg.cn/img_convert/6d998c82b20b55d69684c954f7aed9eb.png)
下面咱们来调用一下:
![](https://img-blog.csdnimg.cn/img_convert/101bea72fc6a88d7265ba8d54410a188.png)
从打印来看其值的读写确实是被委托了,其中特别要强调的是:
![](https://img-blog.csdnimg.cn/img_convert/075f1e6d75c53414e299aca353b806f3.png)
其中这里使用了运算符重载,貌似C++中也有这个关键字,如下:
![](https://img-blog.csdnimg.cn/img_convert/042044a0707221f6837f0d1b5ed6120e.png)
那对于委托属性在实际开发中是有如下4种使用情况的:
1、延迟属性。
2、可观测属性。
3、非空属性。
4、map属性。
延迟属性:
这次先来对延迟属性和非空属性进行学习一下,先来看啥叫延迟属性:它指的是属性只有第一次被访问的时候才会计算,之后则会将之前的计算结果缓存起来供后续调用。下面看个具体例子:
![](https://img-blog.csdnimg.cn/img_convert/ed810c58f4e51e5fe00f12ad9f987fe0.png)
下面来使用一下:
![](https://img-blog.csdnimg.cn/img_convert/b197860b740a4669a50b7a4c67c4ad51.png)
其中lazy是个函数,咱们来看一下它的声明:
![](https://img-blog.csdnimg.cn/img_convert/16f02f9ce6f26aa07ea7986a02f9357e.png)
好奇怪的写法,很显然咱们是将Lambda表达式写在了方法体中,而非方法参数中:
![](https://img-blog.csdnimg.cn/img_convert/b08257a88ce1b57821a622153eb6d759.png)
关于Kotlin的Lambda表达式跟Java8的Lambda表达式是不一样的,这个在之后会专门再学,这里之所以能写在方法体中是有这样一个规则:如果一个方法的最后一个参数是Lambda表达式,则直接可以将它写在方法体中。
另外lazy函数还有另外一个重载的版本,如下:
![](https://img-blog.csdnimg.cn/img_convert/55d119de1aa254a1264cf587fb9271bc.png)
而该模式是被定义成了一个枚举类型:
![](https://img-blog.csdnimg.cn/img_convert/d2c64652cc04ce4c65985e25f33bde60.png)
所以咱们可以简单的使用一下这种重载的方法:
![](https://img-blog.csdnimg.cn/img_convert/c61dfa995d227cb1529f1f7d740e1807.png)
所以下面对这三种模式再总结一下:
1、SYNCHRONIZED:默认情况下,延迟属性的计算是同步的;值只会在一个线程中得到计算,所有的线程都会使用相同的一个结果。
2、PUBLICATION:如果不需要初始化委托的同步,这样多个线程可以同时执行。
3、NONE:如果确定初始化操作只会在一个线程中执行,这样会减少线程安全方面的开销。
非空属性:
先来看一下代码:
![](https://img-blog.csdnimg.cn/img_convert/4d22831cf140b5fc08d218ed490baf40.png)
我们知道对于定义的属性是必须要给它赋初值的,不然就报错了,但是如果没有一个合适的初值能赋给它,此时就可以将它委托给非空属性,如下:
![](https://img-blog.csdnimg.cn/img_convert/a3360c7300dc911a57dd0403be291c0b.png)
其中看一下Delegated:
![](https://img-blog.csdnimg.cn/img_convert/54292155be050d15e827eb48387ac05c.png)
接着来看一下notNull()的说明:
![](https://img-blog.csdnimg.cn/img_convert/f226c7b4e96578d50ce7ec17deac6744.png)
其中它的实现是实例化了NotNullVar,如下:
![](https://img-blog.csdnimg.cn/img_convert/e3f47d50aea972c0213c0bc104443826.png)
而它的定义如下:
![](https://img-blog.csdnimg.cn/img_convert/9dc6eeafc32be151afd5a9fd61617012.png)
它实现了ReadWriteProperty这个接口,如下:
![](https://img-blog.csdnimg.cn/img_convert/9a6547a3a287795424b0fa06700082ec.png)
也就是系统对于委托的这俩函数已经定义好了,关于这个接口在未来还会学习的。好,接下来咱们来使用一下它:
![](https://img-blog.csdnimg.cn/img_convert/04d3a03ecf4ad374036c2159221dc7f8.png)
那。。如果不赋值就直接读取呢?
![](https://img-blog.csdnimg.cn/img_convert/2604d23923006152512c90a69987b9f3.png)
确实如javadoc所说。所以notNull通常适用于那些无法在初始化阶段就确定属性值的场合。