前言
接触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来作为属性委托,来灵活处理具有可变属性集的对象