Kotlin语法基础篇十三:by关键字(手写一个我们自己的lazy函数吧~)
作者:访客发布时间:2023-12-24分类:程序开发学习浏览:99
前言
在上一篇文章我们详细的介绍了Kotlin
中泛型的协变、逆变、类型投影、星投影。这篇文章我们来讲解Kotlin
中另外一个比较重要的知识点委托。委托的运用主要分为两种:委托属性和委托类。关于委托属性,它是一种很好的懒加载模式;而委托类,它是替代继承的一个很好的方式。在Kotlin
中我们使用by
关键字来表示对一个类或属性进行委托,下面我们开始本篇文章的讲解。
1.委托的概念
委托是一种设计模式,它的基本概念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。例如我们下面这段代码就是一个简单的示例:
class NewList<out T>(private val list: MutableList<T>) {
fun isEmpty() = list.isEmpty()
fun add(item: @UnsafeVariance T) = list.add(item)
fun remove(item: @UnsafeVariance T) = list.remove(item)
fun myMethod() {
// some logic
}
}
我们在类NewList中定义了几个简单的方法,我们并没有让NewList自己去实现这些方法的逻辑。而是交给了其构造函数中的属性参数list去处理。
2.委托属性
委托属性的语法:val/var <属性名>: <类型> by <表达式>
在by
关键字后面的表达式是该委托,属性对应的get()
或set()
会被委托给它的getValue()
或setValue()
方法。属性的委托不必实现任何的接口,但是需要提供getValue()
或setValue()
方法。下面我们来看一个具体的示例:
class Delegate {
operator fun getValue(thisRef:Any?, prop:KProperty<*>) : String {
return "$thisRef, name = ${prop.name}"
}
}
class Example {
val p : String by Delegate()
val q : String by Delegate()
}
fun main() {
val e = Example()
println(e.p)
println(e.q)
}
// 输出
com.study.myapplication.bean.Example@4de8b406, name = p
com.study.myapplication.bean.Example@4de8b406, name = q
我们来分析一下这段代码。首先我们创建了一个名为Delegate
的类,然后在Delegate
类内部我们创建了一个使用operator
关键字修饰的方法getValue()
,关于操作符重载的相关知识我们已经在有趣的操作符重载这篇文章中详细的介绍了,这里就不展开讲解了。getValue()
方法中有两个参数:thisRef
和prop
。而thisRef
代表我们被委托的对象实例,prop
中存储了被委托属性的相关信息。
在main()
函数中访问p
属性就是相当于调用Delegate
中的getValue()
方法,如下示例代码:
fun main() {
val e = Example()
println(e)
// 对属性e的访问,等价于下面的调用
val delegate = Delegate()
delegate.getValue(e, e::p)
}
其中e::p
获取的是KProperty
的引用,关于::
的用法我们在后面的文章会详细说明。如果我们仅仅只是委托一个属性的get()
方法,那么该委托属性在声明时只能是val
类型。只有同时委托get()
和set()
我们才能将委托的属性声明为var
类型。这和属性声明时的语法是类似的。如果我们强行将上述示例中被委托的属性声明为var,那么编译器会提示"Type 'Delegate' has no method 'setValue(Example, KProperty<*>, String)' and thus it cannot serve as a delegate for var (read-write property)
意思就是我们只重载get()
被委托的属性是只读的,需要我们去重载set()
,被委托的属性才能被声明为var
类型。下面我们就在Delegate
类中去重载set()
。
class Delegate {
operator fun getValue(thisRef:Any?, prop:KProperty<*>) : String {
return "$thisRef, name = ${prop.name}"
}
operator fun setValue(thisRef:Any?, prop: KProperty<*>, value: String) {
println( "$thisRef, name = ${prop.name}, value = $value")
}
}
class Example {
var p : String by Delegate()
val q : String by Delegate()
}
fun main() {
val e = Example()
println(e.p)
e.p = "by this property"
}
// 输出
com.study.myapplication.bean.Example@1810399e, name = p
com.study.myapplication.bean.Example@1810399e, name = p, value = by this property
可以看到我们在给被委托属性p
赋值时就是调用了Delegate
类中的setValue()
方法。并且在Example
类中我们将被委托属性p
声明为了var
类型。
3.委托属性的真相
上面我们一直在介绍当我们访问被委托属性的get()
就是调用了委托类中的getValue()
方法;访问其set()
就是访问了委托类中的setValue()
方法。对于上述示例,这样一个简单属性委托:
class Example {
var prop : String by Delegate()
}
我们来看一下它反编译成Java
代码的情况,为了方便阅读,这里对代码进行了一些调整:
class Example {
private val prop$delegate = Delegate()
var prop: String
get() = prop$delegate.getValue(this, this::prop)
set(value: String) = prop$delegate.setValue(this, this::prop, value)
}
相信看到这,你已经可以熟练的掌握了属性委托的实现原理了。
4.动手实现一个我们自己的lazy函数吧
在Kotlin
的内置API
中,为我们提供了一个lazy()
高阶函数,我们经常在开发中这么去写:
val property : String by lazy { "property lazy" }
下面我们就来分析一下lazy()
函数,然后我们自己去实现它。lazy()
函数拥有一个函数类型的参数,因为我们在调用lazy()
函数的时候使用了Lambda
表达式。在高阶函数和Lambda表达式的文章中我们已经详细介绍了,在Kotlin
中Lambda
表达式就是函数类型的实例。lazy()
函数中的函数类型的参数拥有一个返回值,该返回值的类型决定了我们被委托属性的类型,我们可以在lazy()
函数的Lambda
表达式中设置任意的类型作为被委托属性的值,我们就可以推断出lazy()
函数是个泛型函数
猜想一 ->lazy()
函数应该长这样:
fun <T> easyLazy(block:() -> T) { }
猜想二 ->lazy()
函数需要返回一个委托类的对象,委托类中的对象重载的get()
的返回值是我们在lazy()
函数中设置的类型,那么我们这个委托类应该长这样:
class Later<T>(private val block: () -> T) {
private var value: Any? = null
operator fun getValue(thisRef:Any?, prop:KProperty<*>) : T {
if(value == null) {
value = block()
}
return value as T
}
}
猜想三 -> lazy()
函数需要完成属性的委托,lazy()
函数本身的返回值需要是个委托类:
fun <T> easyLazy(block:() -> T) : Later<T> {
return Later(block)
}
这样我们一个简单的eazyLazy()
函数就完成了,下面我们写个简单的示例来运用它:
fun main() {
val name : String by easyLazy { "by name" }
println(name)
}
// 输出
by name
就是这么简单,当然内置API
中的lazy()
函数要比这里复杂许多,其还提供了三种不同的使用模式,感兴趣的读者可以自己去阅读下内置的lazy()
函数的实现,这里就不展开介绍了,实现的原理都是一样的。
5.委托类
委托模式已经证明是实现继承的一个很好的替代方式。下面我们就来看一个简单的示例:
class MyList<E>(private val list:MutableList<E>) : MutableList<E> by list {
override val size: Int get() = list.size
}
类委托的语法结构可能看上去不是那么好理解。但是我们要是理解了其真实的实现原理就比较好理解了,下面我们就来分析一下上面这个类委托的示例。首先我们定义了一个MyList
的泛型类,并且让MyList
类实现了MutableList
接口。在MyList
的主构造函数中我们定义了一个类型为:MutableList
名为:list
的属性,然后紧接着我们又在继承类后添加了by list
。而by list
表示list
将会在MyList
中内部存储,并且编译器将生成MutableList
中所有的方法转发给list
。下面我们来看一下MyList
反编译成Java
的代码:
public final class MyList implements List, KMutableList {
private final List list;
public int getSize() {
return this.list.size();
}
public final int size() {
return this.getSize();
}
public MyList(@NotNull List list) {
Intrinsics.checkNotNullParameter(list, "list");
super();
this.list = list;
}
public boolean add(Object element) {
return this.list.add(element);
}
public boolean contains(Object element) {
return this.list.contains(element);
}
public Object get(int index) {
return this.list.get(index);
}
public boolean isEmpty() {
return this.list.isEmpty();
}
public boolean remove(Object element) {
return this.list.remove(element);
}
// ... 省略
}
为了方便阅读,这里将一些方法进行了省略。而在实际开发中,当MutableList
不能满足我们的现有的需求时我们完全可以使用委托来实现,我们可以在MyList
中新增一些我们自定义的方法,让MyList
成为一个新的数据结构,而这也正是我们使用类委托的优势所在。
总结
到这里关于属性委托和类委托的使用我们就介绍完了,事实上两者在语法上并不是很难理解,而难的是我们如何将它们很好的运用在我们的实际开发中。比如委托类,我们需要在什么样的条件下去使用它。
只有掌握了语法的真实含义,我们才能更好的在实际开发中去运用它。下篇文章我们继续讲解Kotin中的基础知识集合,我们下期再见~
- 程序开发学习排行
-
- 1鸿蒙HarmonyOS:Web组件网页白屏检测
- 2HTTPS协议是安全传输,为啥还要再加密?
- 3HarmonyOS鸿蒙应用开发——数据持久化Preferences
- 4记解决MaterialButton背景颜色与设置值不同
- 5鸿蒙HarmonyOS实战-ArkUI组件(RelativeContainer)
- 6鸿蒙HarmonyOS实战-ArkUI组件(Stack)
- 7[Android][NDK][Cmake]一文搞懂Android项目中的Cmake
- 8Android广播如何解决Sending non-protected broadcast问题
- 9鸿蒙HarmonyOS实战-ArkUI组件(mediaquery)
- 最近发表
-
- WooCommerce最好的WordPress常用插件下载博客插件模块的相关产品
- 羊驼机器人最好的WordPress常用插件下载博客插件模块
- IP信息记录器最好的WordPress常用插件下载博客插件模块
- Linkly for WooCommerce最好的WordPress常用插件下载博客插件模块
- 元素聚合器Forms最好的WordPress常用插件下载博客插件模块
- Promaker Chat 最好的WordPress通用插件下载 博客插件模块
- 自动更新发布日期最好的WordPress常用插件下载博客插件模块
- WordPress官方最好的获取回复WordPress常用插件下载博客插件模块
- Img to rss最好的wordpress常用插件下载博客插件模块
- WPMozo为Elementor最好的WordPress常用插件下载博客插件模块添加精简版