Kotlin标准库中,已经自定义了一系列标准委托,包括了大部分有用的委托。
延迟加载(Lazy)
定义
lazy()是一个函数, 接受一个Lambda表达式作为参数, 返回一个Lazy类型的实例,这个实例可以作为一个委托, 实现延迟加载属性(lazy property): 第一次调用 get() 时, 将会执行 lazy() 函数受到的Lambda 表达式,然后会记住这次执行的结果, 以后所有对 get() 的调用都只会简单地返回以前记住的结果。
语法结构
案例分析
针对延迟加载lazy,我们需要知道两个结论。
-
延迟属性只有在访问的时候才会被初始化。
-
延迟属性只会初始化一次。
参考代码:
针对以上代码,我们在7行打个断点,然后debug运行程序。可以看到【normalValue】已经赋值成功,而【lazyValue】则提示“Lazy value not initialized yet.”。
参考截图:
之所以出现现在的情况,因为我们还没有访问到【lazyValue】,我们接着执行完第17行,将会看到lazyValue被初始化了。
参考截图:
我们接着执行完所有代码,看结果输出:
可以看到【println("lazyValue进行初始化")】只被输出了一次。所以,延迟属性lazy只初始化一次。
可观察属性(Observable)
定义
Delegates.observable()函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler)。这种形式的委托,采用了观察者模式,其会检测可观察属性的变化,当被观察属性的setter()方法被调用的时候,响应器(handler)都会被调用(在属性赋值处理完成之后)并自动执行执行的lambda表达式,同时响应器会收到三个参数:被赋值的属性, 赋值前的旧属性值, 以及赋值后的新属性值。
语法结构
其中Lambda表达式(变化事件的响应器)会在变量的setter()被调用的时候触发。
Lambda里面可以获取三个参数:
prop-被赋值的属性;
old-赋值前的旧属性值;
new-赋值后的新属性值;
案例分析
参考代码:
针对以上代码,第13行和第16行代码对属性进行了修改,所以我们的属性变化事件响应器被触发了2次。
Vetoable
定义
Delegates.vetoable()函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler),是可观察属性(Observable)的一个特例,不同的是在响应器指定的自动执行执行的lambda表达式中在保存新值之前做一些条件判断,来决定是否将新值保存。
语法结构
Lambda 返回true:变量会被修改
Lambda返回false:变量不会被修改
案例分析
参考代码:
结果分析:
针对以上代码,代码在第15行和第19行,对userName进行了修改,每次修改都触发了Lambda表达式的执行。但是第15行新的属性值【"heima"】不满足【new.contains("黑马")】,所以没有赋值,所以16行输出结果是【userName当前属性值:黑马程序员】。
notNull
定义
对于一个不可为“non-null”的变量,我们需要保证它被正确的赋值。赋值操作可以在变量定义的时候,也可以后续代码里面进行赋值。我们只需要在变量后面使用notNull属性委托,编译器就允许我们后续进行赋值。
加了notNull的属性委托,编译器允许我们后续赋值,那我们也有可能忘记赋值,这样使用变量的时候就会报空指针。那有的同学可能会说,我已经养成了良好的习惯,在使用变量之前会进行非空判断,这样的做法会导致有大量的非空判断,这是不美观的。其实这个时候即使做非空判断也会抛出异常(UninitializedPropertyAccessException)。因为notNull的属性委托,要求变量被引用的时候被赋值。
语法结构
案例分析
参考代码:
结果分析:
针对以上代码的第11行,因为我们使用了notNull的属性委托,所以编译器允许【userName】后续进行赋值。
针对以上代码的第16行对【user.userName】进行非空判断,然后在第17行进行使用,感觉上不会有什么问题,但是结果大家看到了,报了空指针异常。说明,我们用了notNull属性委托之后,在第16行的时候用到了【user.userName】,这个时候如果我们没有赋值就会抛出异常,提示“lateinit propety userName has not been initialized”。
将多个属性保存在一个map内
定义
Kotlin的map委托,提供了map和对象一一绑定关系,就是map的值可以决定对象的值,修改map的值也可以影响对象的值。但是这一切需要满足,map的key和属性的名称保证一致。
语法结构
val/var 变量: 变量类型by 实现Map接口的对象
案例分析
参考代码:
提示:
针对以上代码的第7行用到的【MutableMap】,也可以改为其他map,但是如果要想修改map集合里面的值,必须使用MutableMap接口对象。
网友评论