驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
Kotlin系列:扩展函数、扩展方法、中缀表达式
/  

Kotlin系列:扩展函数、扩展方法、中缀表达式

开篇

作为一个 javer 当我看到类似这样的代码的时候,我的内心还是有小小的震撼的。

val someday = "today" afterDay 4

当看到这段代码的时候,你有没有懵逼掉?

  • 这段代码能执行?至少我确定在 Java 中不行。
  • 这段代码猜出的意思是今天往后推 4 天是哪天
  • today 后面没有 . 却是出现了一个 afterDay 这个莫名其妙的东西

那我们看看他是怎么实现的了!

扩展函数/属性

在写出上述代码之前我们需要了解一个 Java 中没有的东西,那就是 扩展函数

扩展函数的重点就是“扩展”,它扩展的就是某个类的方法、属性等,可以为原本不存在于某个类的方法和属性做扩展。

扩展函数

我们来举 2 个例子,比如对于 String 期望能有一个 join 将字段拼接起来,

val res = "123".append("abc")//结果是123abc

但是 JDK 中的 String 中没有这个 append 方法,同时这个 String 是 JDK 的包,我们也没有很好的办法去修改源码,那么此时扩展函数的作用就来了。

fun String.append(that: String): String {
    return this + that
}

上面的就是扩展函数

  • String.append 表示的就是为 String 这个类扩展 append 这个方法
  • (that:String) 就是一个形参

上面的例子很好理解了吧?

扩展属性

对于某个字符串,我们希望他有个 属性 (注意不是方法),比如 a 出现的次数 aTimes

假如 aTimes 是一个方法,可以这么实现

fun String.aTimes(): Int {
    return this.toCharArray().filter { it == 'a' }.count()
}

假如 aTimes 是一个 只读属性 ,那么如何实现?

val String.aTimes: Int
    get() = this.toCharArray().filter { it == 'a' }.count()
  • 通过关键字 val 修饰 只读属性
  • 实现 get() 方法
  • : 后的是获取的类型

那么假如某个属性,既是 能读又能写 的,如何实现了?

var StringBuilder.lastChar: Char
    get() =  get(length - 1)
    set(value) {
        this.setCharAt(length -1, value)
    }
  • 通过 var 修饰
  • : 后的是获取和设置的类型。

当我们有了上述基础后,我们就来看看 中缀表达式 的实现。

中缀表达式

kotlin 中有一个关键字叫做 infix ,很多翻译叫做 中缀表达式 ,我们就先这么称呼着,名字而已,约定俗成。

我们先回顾下文章最先出现的代码:

val someday = "today" afterDay 4

假如 afterDay 是点出来的方法,我们都可以对他进行实现了,比如说

fun String.afterDay(offset: Int): LocalDate {
    return when (this) {
        "today" -> LocalDate.now().plusDays(offset.toLong())
        //省略其他
        else -> throw IllegalArgumentException("参数不合法")
    }
}

//调用的时候可以
val someDay = "toDay".afterDay(4)

但是我们想要的不是 . 出来的,而是把除 .() 这些符号去掉,只保留最核心的元素。

其实通过 infix 可以很简答的实现

//中缀表达式:用于两个对象间的运算
infix fun String.afterDay(that: Int): LocalDate {
    return when (this) {
        "today" -> LocalDate.now().plusDays(that.toLong())
        "yesterday" -> LocalDate.now().plusDays(that.toLong() - 1)
        "tomorrow" -> LocalDate.now().plusDays(that.toLong() + 1)
        else -> LocalDate.now().plusDays(that.toLong())
    }
}

//这样调用
val someday = "today" afterDay 4

上述代码中只是加了关键字 infix ,那么就可以不通过 . 来调用方法,而是通过 空格+方法名 的形式调用。

结语

从本质上来说,其实这些都是语法糖,掌握了这些语法糖,当你在可能使用到的地方,确实是有点

骐骥一跃,不能十步。驽马十驾,功在不舍。