扩展函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Ext.kt
package com.boycoder.chapter06

/*
① ② ③ ④
↓ ↓ ↓ ↓ */
fun String.lastElement(): Char? {
// ⑤
// ↓
if (this.isEmpty()) {
return null
}

return this[length - 1]
}

// 使用扩展函数
fun main() {
val msg = "Hello Wolrd"
// lastElement就像String的成员方法一样可以直接调用
val last = msg.lastElement() // last = d
}

在整个扩展函数的方法体当中,this 都是可以省略的。

实现原理

反编译后的 Java 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public final class ExtKt {
// ①
public static final Character lastElement(String $this) {
CharSequence var1 = (CharSequence)$this;
if (var1.length() == 0) {
return null
}

return var1.charAt(var1.length() - 1);
}
}

public static final void main() {
String msg = "Hello Wolrd";
// ②
// ↓
Character last = ExtKt.lastElement(msg);
}

扩展属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 接收者类型
// ↓
val String.lastElement: Char?
get() = if (isEmpty()) {
null
} else {
get(length - 1)
}

fun main() {
val msg = "Hello Wolrd"
// lastElement就像String的成员属性一样可以直接调用
val last = msg.lastElement // last = d
}

反编译后的 Java 代码几乎和我们前面扩展函数的一模一样。

Kotlin 的扩展表面上看起来是为一个类扩展了新的成员,但是本质上,它还是静态方法。

扩展的能力边界

扩展能做什么?

它最主要的用途,就是用来取代 Java 当中的各种工具类,比如 StringUtils、DateUtils 等等。

所有 Java 工具类能做的事情,Kotlin 扩展函数都可以做,并且可以做得更好。扩展函数的优势在于,开发工具可以在编写代码的时候智能提示。

扩展不能做什么?

  • Kotlin 扩展不是真正的类成员,因此它无法被它的子类重写。
  • 扩展属性无法存储状态。
  • 扩展的访问作用域仅限于两个地方。第一,定义处的成员;第二,接收者类型的公开成员。

使用场景

主动使用扩展,通过它来优化软件架构。

对复杂的类进行职责划分,关注点分离。让类的核心尽量简单易懂,而让类的功能性属性与方法以扩展的形式存在于类的外部。比如我们的String.kt与Strings.kt。

被动使用扩展,提升可读性与开发效率。

当我们无法修改外部的 SDK 时,对于重复的代码模式,我们将其以扩展的方式封装起来,提供给对应的接收者类型,比如 view.updateMargin()。

示例:通过 updateMargin() 这个扩展函数,可以大大简化 Android 当中的 margin 更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
inline fun <reified T : ViewGroup.LayoutParams> View.updateLayoutParams(block: T.() -> Unit) {
val params = layoutParams as T
block(params)
layoutParams = params
}

fun View.updateMargin(left: Int? = null, top: Int? = null, right: Int? = null, bottom: Int? = null) {
(layoutParams as? ViewGroup.MarginLayoutParams)?.let { param ->
updateLayoutParams<ViewGroup.MarginLayoutParams> {
left?.let {
marginStart = left
}

right?.let {
marginEnd = right
}

top?.let {
topMargin = top
}

bottom?.let {
bottomMargin = bottom
}
}
}
}
1
view.updateMargin(top = 100, bottom = 100)

小结

  • Kotlin 的扩展,从语法角度来看,分为扩展函数和扩展属性。定义扩展的方式,只是比普通函数、属性多了一个“扩展接收者”而已。
  • 从作用域角度来看,分为顶层扩展和类内扩展。
  • 从本质上来看,扩展函数和扩展属性,它们都是 Java 静态方法,与 Java 当中的工具类别无二致。对比 Java 工具类,扩展最大的优势就在于,IDE 可以为我们提供代码补全功能。
  • 从能力的角度来看,Kotlin 扩展一共有三个限制,分别是:扩展无法被重写;扩展属性无法存储状态;扩展的作用域有限,无法访问私有成员。
  • 从使用场景的角度来看,Kotlin 扩展主要有两个使用场景,分别是:关注点分离,优化代码架构;消灭模板代码,提高可读性和开发效率。

关注我