Kotlin进阶学习-第四篇

Kotlin进阶学习—第四篇

读者学习本篇文章前请先学习之前的文章

Kotlin系列已更新:

Kotlin基础学习-入门篇

Kotlin基础学习-第二篇

Kotlin进阶学习-第三篇

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则等价于JavaObject

改进第三篇中的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类,并且必须实现getset方法

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
    }
}

上述的getset,必须使用重载符operator修饰,其中都有两个参数,前一个参数为在哪个类可以使用委托,写成MyClass代表只可在MyClass中使用,第二个参数KProperty<*>Kotlin的一个属性操作类,用于获取各种属性相关的值,当前用不到,但必须声明,<*>类似于java<?>,靠程序员自己去定义,只是上述演示使用星

其原理具体流程如下:当我们给p赋值时,则会调用到DelegatesetValue方法,若获取时则会调用getValue方法。

注意:若上述p声明为val,则不需要实现setValue方法

lazy

lazyKotlin提供的一种懒加载技术,使用如下:

val p by lazy {
    //初始化操作
}

只有当真正调用p是才会执行Lambda中的代码,对p进行初始化

上述学习了委托,则可对lazy的原理进行分析:

调用lazy函数会创建一个Delegate对象,在构造Delegate时可以将Lambda传给Delegate,因此在后续我们使用p,调用DelegategetValue方法时就可以先执行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使用

下面实现StringstartsWith(),最终使用效果类似于"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进行了了解,其有两个属性firstsecond,对应keyvalue

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}{评论,你的意见是我进步的财富!}

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值