首页 > 其他分享 >Android kotlin 类委托 by,by lazy关键

Android kotlin 类委托 by,by lazy关键

时间:2022-11-04 11:35:12浏览次数:68  
标签:lazy val kotlin value fun Android property class 属性


前言

接触kotlin语言也有几年时间了。日常开发工作中也推荐使用kotlin,但是对于一些kotlin语言语法的细节没有进行系统学习。碎片的知识点让工作中屡屡碰壁,前些天开始学习compose时候,意识到基础没有打好,最近也在分阶段的复习kotlin语言语法知识点。并统一输出。

类委托:使用“by”关键

关键字by解决的问题是:

有继承实现导致的脆弱性,基类被子类继承并对某些功能进行了扩展,这样子类就依赖了父类实现细节。随着不断迭代,父类中实现细节会随着迭代而被修改。早期对父类实现细节的假设也会失效,最终导致程序以不正确的行为而告终。所以kotin 设计初衷就识别这样的问题。将类默认访问修饰符定义为final ,不能继承。如果要对不能继承的类进行拓展,装饰设计模式提供现成的解决方案。

by关键字,底层的原理就是系统会帮我们自动生成一些​​装饰设计模式​​需要实现的模板代码。使类结构更清晰

上面是手动实现代理,二下面例子是使用by关键字,由系统生成装设模式的模板代码
/**
* creat time 2022/6/13 下午9:00
* author kuaidao
* email : [email protected]
* 装饰器模式:与被装饰类实现同样的接口,持有被装饰类对象。
*/
class DelegatingCollection<T>:Collection<T>{

val _list=ArrayList<T>()
override val size: Int
get() = _list.size

override fun contains(element: T): Boolean {
return _list.contains(element)
}

override fun containsAll(elements: Collection<T>): Boolean {
return _list.containsAll(elements)
}

override fun isEmpty(): Boolean {
return _list.isEmpty()
}

override fun iterator(): Iterator<T> {
return _list.iterator()
}

}
//可以针对代理接口,使用by关键字

class DelegateCollect( innerList:ArrayList<T> = arrayListOf<T>()):Collection<T> by innerList{

}

​​装饰器设计模式​​:
本质创建一个新类,实现与原始类一样的接口,并将原始类用一个字段缓存,具有一样行为的代码由原始类实现,在原始类行为代码调动前后可以添加一些其他的装饰代码。这样可以保持原始类api不变的情况下,对原始类进行一个功能扩展,即装饰类的作用

委托属性:可重用属性访问逻辑

委托属性:是将属性的访问器逻辑委托给一个辅助对象称委托),并默认提供属性变化监听接口,可以定义一个监听回调在属性更改时收到通知事件。

基本语法

class Foo{

val type:Type by Deletegate()
}

//规定委托对象必须包含setValue,getValue方法(对应var类型属性)
class Deletegate{

operator fun getValue()
operator fun setValue(type:Type)

}

fun main(){
val foo =Foo()
foo.type //等价于 deletegate.getValue()
foo.type = type类型值 //等价于 deltegate.setValue()
}

惰性初始化和 by lazy{ }

惰性初始化对一线开发也不会陌生,fragment 懒加载。对于资源获取比较耗时的操作,一般处理方式会进行延迟初始化。第一次用到才会初始化,之后再次用到会直接将已经初始化好了的对象引用或者资源返回。不再进行初始化

import java.util.*
/**
* creat time 2022/6/14 上午10:29
* author kuaidao
* email : [email protected]
* 惰性初始化
*/
class SupportProperty{

private val _resources:List<Resources>?=null
private val _resources2:List<Resources>?=null
private val _resources3:List<Resources>?=null

val resources:List<Resources>
get() {
if(_resources==null){
_resources=initResources()
}else{
_resources
}
}

}

class Resources{ }

fun main(args: Array<String>) {
val supportProperty=SupportProperty()
//首次访问会初始化资源
supportProperty.resources
//第二次访问就会使用第一次加载的资源
supportProperty.resources
}

这里使用了支持属性技术,一个_resource用来存储资源缓存,另一个用来获取资源,前一个_resources 可以为null,而后一个不会为null,通过代码展示可以看到里面仅仅对resource 做了惰性初始化处理,那么如果一个类中需要对多个资源访问全部进行惰性初始化,靠堆代码实现并不是好的方案。kotlin by lazy{} 帮我们提供了惰性初始化方案。

同样的代码,一行搞定了惰性初始化。

class SupportProperty{

private val _resources:List<Resources> by lazy {initResources()}
}

lazy的参数是一个lambda表达式,表达式返回的对象需要实现getValue,setValue才能这么用。默认情况下lazy是线程安全的。这里系统提供了三种线程安全模式设置的接口

public enum class LazyThreadSafetyMode {

/**
* Locks are used to ensure that only a single thread can initialize the [Lazy] instance.
*/
SYNCHRONIZED,

/**
* Initializer function can be called several times on concurrent access to uninitialized [Lazy] instance value,
* but only the first returned value will be used as the value of [Lazy] instance.
*/
PUBLICATION,

/**
* No locks are used to synchronize an access to the [Lazy] instance value; if the instance is accessed from multiple threads, its behavior is undefined.
*
* This mode should not be used unless the [Lazy] instance is guaranteed never to be initialized from more than one thread.
*/
NONE,
}

例如:

private  val _resources:List<Resources> by lazy(LazyThreadSafetyMode.NONE) {initResources()}

手动实现委托属性

如下代码,通过提供PropertyChangeSupport 属性变更通知监听接口,在属性被修改的时候,发送 firePropertyChange( )变更事件,设置一个 PropertyChangeListener()监听,就可以在属性修改后收到通知。

/**
* creat time 2022/6/13 下午10:10
* author kuaidao
* email : [email protected]
* 属性变化监听,需要对每一个可能变化的属性增加变化发送变化事件,并且bean需要继承属性变化监听接口
*/

open class propertyChangeAware{
val changeSupport=PropertyChangeSupport(this)


fun addChangeSupportListener(listener: PropertyChangeListener){
changeSupport.addPropertyChangeListener(listener)
}

fun removeSupportChangeListener(listener: PropertyChangeListener){
changeSupport.removePropertyChangeListener(listener)
}
}

class Persion(val name:String,age:Int):propertyChangeAware(){
var age:Int=age
set(value){
val oldValue=field
field=value
changeSupport.firePropertyChange("age",oldValue, field)
}
}

fun main(args: Array<String>) {
val p=Persion("kuangdao",18)
println(p.toString() +"真实值")
p.addChangeSupportListener(object : PropertyChangeListener {
override fun propertyChange(evt: PropertyChangeEvent) {
println("propter =${evt.propertyName} oldvalue=${evt
.oldValue} newValue=${evt.newValue}")
}
})
println("调整成100")
p.age=100
}

这里面只对age属性做了一个变更监听,如果需要对多个属性变动做监听,可以将通过代码抽取出来进行复用。

class ObservableProperty<T>(val name:String,val value:T,val changeSupport:PropertyChangeSupport){

fun getvalue():T=value
fun setValue(newValue){
val oldValue=value
value=newValue
changeSupport.firePropertyChange("age",oldValue, newValue)
}
}

}

如上而Kotin 的委托属性功能可以让你拜托这些样板代码。kotlin实现代码路径
common/kotlin/properties/ObservableProperty.kt 包中

public abstract class ObservableProperty<V>(initialValue: V) : ReadWriteProperty<Any?, V> {
private var value = initialValue

/**
* The callback which is called before a change to the property value is attempted.
* The value of the property hasn't been changed yet, when this callback is invoked.
* If the callback returns `true` the value of the property is being set to the new value,
* and if the callback returns `false` the new value is discarded and the property remains its old value.
*/
protected open fun beforeChange(property: KProperty<*>, oldValue: V, newValue: V): Boolean = true

/**
* The callback which is called after the change of the property is made. The value of the property
* has already been changed when this callback is invoked.
*/
protected open fun afterChange(property: KProperty<*>, oldValue: V, newValue: V): Unit {}

public override fun getValue(thisRef: Any?, property: KProperty<*>): V {
return value
}

public override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
val oldValue = this.value
if (!beforeChange(property, oldValue, value)) {
return
}
this.value = value
afterChange(property, oldValue, value)
}
}

委托属性的变换规则

class Foo{

val type:Type by Deletegate()
}

Deletegate实例会被保存到一个隐藏的属性中,称为 ,编译器也将用一个Kproperty类型对象来代表这个属性的称为,编译器生成代码

public final class Foo {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Foo.class, "type", "getType()I", 0))};
@NotNull
private final Deletegate type$delegate = new Deletegate();

public final int getType() {
return this.type$delegate.getValue(this, $$delegatedProperties[0]);
}
}

编译代码回调将对属性的访问直接转接到代理对象上面 type$delegate.getValue( this,属性)
这个机制可以方便让我们对属性的访问做一些操作,比方说自定义属性存储的位置(map,数据库),在访问该属性时做一些验证等。

在map中保存属性值
/**
* creat time 2022/6/14 上午11:23
* author kuaidao
* email : [email protected]
*/
class Person{

private val _resource = hashMapOf<String,String>()

fun setAttribe(name:String,value:String){
_resource.put(name,value)
}

val name:String
get() {
return _resource["name"]!!
}

val company:String by _resource
}


fun main(args: Array<String>) {
val p=Person();
val data = mapOf( "name" to "kuaiao","company" to "codeReview")
for((key,value) in data){
p.setAttribe(key,value)
}
//println(p.name)
println(p.company)
}

属性对应着map中的key。

总结:

委托属性可以用来重用逻辑,这些逻辑控制如何存储、初始化、访问和修改属性值,lazy标准库函数提供一种实现惰性初始化属性的简单方法,Delegates.observable函数可以用来添加属性更改的观察者,委托属性可以使用任意map来作为属性委托,来灵活处理具有可变属性集的对象

​1.kotlin简单语法练习​


标签:lazy,val,kotlin,value,fun,Android,property,class,属性
From: https://blog.51cto.com/u_15861646/5823166

相关文章

  • 【AGC】SDK未经用户同意获取AndroidID问题
     1.AGC-接入agc的sdk检测到未经用户同意获取AndroidId的问题。问题背景:开发者接入华为性能管理、崩溃服务、华为分析等SDK后上架小米应用商店被拒,称检测到未经用户同意获取......
  • Android实现页面跳转
    Android实现页面跳转​​MainActivity​​​绑定​​activity_main.xml​​​,​​Main2Activity​​​绑定​​activity_main2.xml​​​,则可以实现从​​activity_main.xml......
  • Android平台GB28181接入端如何对接UVC摄像头?
    我们在对接Android平台GB28181接入的时候,有公司提出这样的需求,除了采集执法记录仪摄像头自带的数据外,还想通过执法记录仪采集外接UVC摄像头。实际上,这块对我们来说有点炒冷......
  • Android实现Socket通信
    效果图  用Idea创建一个java工程importjava.io.*;importjava.net.*;publicclassMain{publicstaticvoidmain(String[]args)throwsIOException{......
  • Android10 dex2oat实践
    最近看到一篇博客:Android性能优化之Android10+dex2oat实践,对这个优化很感兴趣,打算研究研究能否接入到项目中。不过该博客只讲述了思路,没有给完整源码。本项目参考该博......
  • Android分区简述
    Android分区在Android目录下,总是能够看到system,data等文件夹,这里简单对Android分区的概念进行简单的整理。在PMS进行初始化的时候,在扫描阶段会去创建分区的子目录,例如:/......
  • [Android开发学iOS系列] Auto Layout
    [Android开发学iOS系列]AutoLayout内容:介绍什么是AutoLayout.基本使用方法在代码中写约束的方法AutoLayout的原理尺寸和优先级AutoLayout的使用细则重......
  • Androidstudio快速解决Gradlesdependencycachemaybecorrupt和Gradle配置gradle-3.*-al
    Error:Failedtoopenzipfile. Gradle'sdependencycachemaybecorrupt(thissometimesoccursafteranetworkconnectiontimeout.) Re-downloaddependencies......
  • Android Studio编程第一篇:反应时间测试(RTI)
    目标参与者必须选择并按住屏幕底部的一个按钮。上面有一个圆圈(一个用于简单模式,五个用于五种选择模式)。在每一种情况下,其中一个圆圈中都会出现一个黄色的圆点,参与者必须尽......
  • Android Studio编程遇到的问题和常用模式总结
    起源一个学精神医科的朋友写论文需要做交互性的实验,让我帮忙做一套APP,主要用于测试病人反应速度,需要在移动端上实现,python-for-android部署起来很折腾,做成网页版的话还需......