Kotlin进阶学习—第四篇
读者学习本篇文章前请先学习之前的文章
Kotlin
系列已更新:
泛型
笔者之前写过一篇关于Java
泛型的文章
Kotlin
的泛型和Java
的泛型有同有异,因此本篇文章只学习泛型的基础用法,基础和原理请看上面文章。
泛型类
声明如下:
class MyClass<T> {
private var value: T? = null
public fun setValue(value: T) {
this.value = value
}
}
使用如下:
var myClass = MyClass<Int>()
myClass.setValue(123)
声明对象时传入Int
,属性value
的类型则为Int
泛型方法
声明如下:
public fun <T> method(param: T): T{
return param
}
使用如下:
method<Int>(123)
限定泛型类型
有时候程序员相对泛型的类型进行限制,则需借助继承来实现
声明如下:
fun <T : Number> method(param: T): T{
return param
}
若不指定类型,T
会被类型擦除为Any?
,?
代表可为空,Any
则等价于Java
的Object
改进第三篇中的build函数
在第三篇文章中,我们编写了一个StringBuilder
的拓展函数build
,其用法与apply
一样
fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder {
block()
return this
}
上述写法中build
函数只能对StringBuilder
生效,若想其他类使用则需下面写法,声明顶层方法:
fun <T> T.bulid(block: T.() -> Unit): T {
block()
return this
}
这时任何对象都可使用build
函数,效果与apply
等价
委托
委托是一种设计模式,基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。
Java
没有再语法层面对委托模式进行支持,而kotlin
是支持的。
委托分为两种类委托和委托属性
类委托
类委托则是将一个类的具体实现委托给另一个类去完成
之前我们学习了set
,如果我们想实现自己的set
,借助委托模式如下:
class MySet<T>(val helpSet: HashSet<T>) : Set<T> {
override val size: Int
get() = helpSet.size
override fun contains(element: T) = helpSet.contains(element)
override fun containsAll(elements: Collection<T>) = helpSet.containsAll(elements)
override fun isEmpty() = helpSet.isEmpty()
override fun iterator() = helpSet.iterator()
}
MySet
全部的实现都是依靠委托类HashSet
去完成的,那直接调用HashSet
不可以吗?可以,但是委托模式允许我们加入独有的方法,MySet
则可成为一个全新的数据结构类,这也是委托模式存在的意义。
上述写法是存在弊端的,若set
接口中有非常多的方法,那岂不是MySet
都要实现一遍,Java
中并没有提供解决方法,但Kotlin
可以借助by
关键字来解决上述问题
借助by,上述代码可改为:
class MySet<T>(val helpSet: HashSet<T>) : Set<T> by helpSet{
//定义自己的新方法
fun helloWorld() = println("Hello world")
//重写方法,错误示范,只是举例
override fun isEmpty(): Boolean {
return false;
}
}
属性委托
类委托是将具体实现委托给另一个类去完成,委托属性则是一个属性的具体实现委托给另一个类去完成
其使用如下:
创建MyClass
并声明需要委托的属性
class MyClass {
var p by Delegate()
}
创建Delegate
类,并且必须实现get
和set
方法
class Delegate {
var propValue: Any? = null
operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
return propValue
}
operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?) {
propValue = value
}
}
上述的get
和set
,必须使用重载符operator
修饰,其中都有两个参数,前一个参数为在哪个类可以使用委托,写成MyClass
代表只可在MyClass
中使用,第二个参数KProperty<*>
是Kotlin
的一个属性操作类,用于获取各种属性相关的值,当前用不到,但必须声明,<*>
类似于java
的<?>
,靠程序员自己去定义,只是上述演示使用星
其原理具体流程如下:当我们给p
赋值时,则会调用到Delegate
的setValue
方法,若获取时则会调用getValue
方法。
注意:若上述p
声明为val
,则不需要实现setValue
方法
lazy
lazy
时Kotlin
提供的一种懒加载技术,使用如下:
val p by lazy {
//初始化操作
}
只有当真正调用p
是才会执行Lambda
中的代码,对p
进行初始化
上述学习了委托,则可对lazy
的原理进行分析:
调用lazy
函数会创建一个Delegate
对象,在构造Delegate
时可以将Lambda
传给Delegate
,因此在后续我们使用p
,调用Delegate
的getValue
方法时就可以先执行Lambda
再返回value
。
下面仿照lazy
实现一个简单的懒加载:
创建Later.kt
class Later<T>(val block: () -> T) {
var value: Any? = null
operator fun getValue(any: Any?, prop: KProperty<*>): T {
if (value == null) {
value = block()
}
return value as T
}
}
//顶层方法,任何地方都可以调用
fun <T> later(block: () -> T) = Later(block)
使用如下:
fun main() {
val p by later {
"abc"
}
println(p + " " + 2)
}
//打印结果 abc 2
上述懒加载只是简单的实现,在开发过程还需使用内置的lazy
,其内部有严谨的判空机制。
infix
在前几篇文章中学习了map
的使用,其有一种初始化map
的方法如下:
mapOf<String, Int>("A" to 1, "B" to 2)
之前没有讲解"A" to 1
这种方式的原理,本节对to
的原理进行解析:
to
不是关键字,to
是一个函数,“A” to 1这种写法是Kotlin提供的语法糖:infix函数,其等价于A.to(1)
的写法
infix使用
下面实现String
的startsWith()
,最终使用效果类似于"Hello World" startsWith "Hello"
使用后infix
定义一个beginsWith
函数:
infix fun String.beginsWith(prefix: String) = startsWith(prefix)
使用如下:
if ("Hello World" beginsWith "Hello") {
//处理逻辑
}
注意:infix
函数只能接受一个参数。
下面再使用一个例子对容器的contains
方法进行简化:
infix fun <T> Collection<T>.has(element: T) = contains(element)
使用如下:
val list = listOf<String>("a", "b", "c")
if (list has "b") {
//处理逻辑
}
to和Pair对象原理
查看to
函数的源码:
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
在上篇文章中我们也对Pair
进行了了解,其有两个属性first
,second
,对应key
,value
public data class Pair<out A, out B>(
public val first: A,
public val second: B
) : Serializable {
...
}
到这本篇结束
✨ 原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下
👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
⭐️ 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!