在使用 Java 的时候,我们经常使用诸如 StringUtil、DateUtil 等工具类,代码写起来比较冗长。举个例子,获取一个字符串的第一个字符值、最后一个字符值。如果我们用 Java 代码来写通常要先声明一个 StingUtil 类,然后在里面写相应的方法。那么,我们能不能直接在 String 类上增加获取第一个字符值、最后一个字符值的方法呢?非常遗憾的是,在 Java 中我们无法给 String 类增加一个自定义方法。因为 String 类是 JDK 中内置的基础类,而且为 final,不能修改。所以,在 Java 中通常需要引入一个工具类来封装对应的方法,而不是修改或继承 String 类。
而在 Kotlin 中,情况就完全不一样了,我们完全可以自由扩展任何类的方法和属性。在不修改原类的情况下,Kotlin 能给一个类扩展新功能而无须继承该类。
1. 扩展函数
Kotlin 中提供了非常实用的扩展函数功能。它允许你在不修改原始类的情况下,为该类添加新的函数。通过扩展函数,你可以为任何类添加新的行为,包括标准库中的类、第三方库中的类,甚至是你自己定义的类。
1.1 扩展函数语法
要创建一个扩展函数,你需要使用 fun 关键字,后面跟着接收者类型(即你要为哪个类添加函数),然后是函数名和函数体。在函数体内,你可以像操作普通函数一样使用接收者对象的成员。下面通过两个例子来感受下扩展函数的使用。
1.1.1 给 String 增加扩展函数
1 | /** |
上面代码中扩展函数的语法说明如下图所示:
1.1.2 给 List 增加扩展函数
在 Kotlin 中,List 是默认支持 filter() 函数的。那么这个 filter() 函数是如何实现的呢?下面我们分别通过 Java 和 Kotlin 来实现 filter() 函数,通过对比来感受下 Kotlin 扩展功能的简单、优雅性。
我们先来看看 Java 中的实现方式!首先,我们会去声明一个 ListUtil 类,里面实现一个 List filter(List list, Predicate p) 方法,代码如下:
1 | import java.util.ArrayList; |
然后,我们在代码中这样使用这个 filter() 方法:
1 | List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); |
为了调用 filter() 方法,我们还要声明一个 Listutil 对象,这样显得比较烦。能不能直接像下面这样调用呢?
1 | list.filter { it % 2 == 1 } |
答案是肯定的,只不过必须是在 Kotlin 中,而不是在 Java 中。下面我们就来用 Kotlin 中的扩展函数为 List 扩展一个 filter() 函数并进行调用,代码如下:
1 | /** |
上面代码中扩展函数的语法说明如下图所示:
然后我们在代码中只需要这样调用即可:
1 | /** |
1.2 扩展函数限制
虽然 Kotlin 扩展函数非常方便,但我们也需要注意以下限制:
序号 | 限制描述 | 原因描述 |
---|---|---|
1 | 扩展函数不能访问类的私有成员 | 这是因为扩展函数并没有修改原始类的代码,它们只是在调用时被插入到类的实例上,因此无法访问私有成员。 |
2 | 与 Java 不同,Kotlin 不支持静态成员的扩展 | 因为扩展函数本质上是在类的实例上调用的,而静态成员不属于类的实例,所以无法扩展静态成员。 |
3 | 如果类中已经存在同名的函数,那么扩展函数将无法被调用 | 这是因为编译器会优先选择类中已经存在的同名函数。 |
4 | 扩展函数不能重载操作符 | 这是因为操作符重载需要在类内部进行,扩展函数无法访问类内部的成员。 |
5 | 扩展函数不能访问类的泛型参数 | 这是因为扩展函数不在类的作用域内,无法访问类的泛型参数。 |
总的来说,虽然 Kotlin 扩展函数非常方便,但需要注意它们的限制,避免出现意外的错误。
2. 扩展属性
Kotlin 中的扩展属性是一种特殊的扩展函数,它允许我们为现有的类添加新的属性,而无需修改类的源代码。通过扩展属性,我们可以在不继承或修改类的情况下,为类添加额外的属性。
2.1. 扩展属性语法
扩展属性是通过增加 Get、Set 方法实现,没有幕后字段 Filed。本质上来讲,扩展属性并没有真的为类添加了属性,只是通过 Get、Set 方法来访问已有类的属性或方法。因此,扩展属性不能有后备字段,也不能被初始化。例如,我们给 MutableList 扩展两个属性:firstElement 和 lastElement,实现代码如下:
1 | var <T> MutableList<T>.firstElement: T |
上面代码中扩展属性的语法说明如下图所示:
然后就可以在代码中直接使用扩展的属性了:
1 | val data = mutableListOf(1, 2, 3, 4, 5, 6, 7) |
✦ 注意: 扩展属性允许定义在类或者 Kotlin 文件中,不允许定义在函数中。
2.2 扩展属性限制
Kotlin 的扩展属性虽然方便易用,但是也有一些限制:
序号 | 限制描述 | 原因描述 |
---|---|---|
1 | 扩展属性不允许初始化 | 与类属性不同,扩展属性不允许在定义时为其指定初始值,因为扩展属性不属于原有类的成员变量,不能直接修改原有类的内部结构。 |
2 | 扩展属性不能被继承 | 与类成员属性不同,扩展属性是静态定义的,无法被子类继承或重写。 |
3 | 扩展属性不能直接访问类的私有或受保护成员 | 与扩展函数类似,扩展属性无法访问原有类的私有或受保护成员,因为扩展属性是在该类之外定义的。 |
4 | 扩展属性不能重载 | 与类成员函数不同,扩展属性不能被重载,因为扩展属性是静态定义的,无法根据不同的参数类型进行重载。 |
5 | 扩展属性不能覆盖原有类的属性 | 与类成员属性不同,扩展属性不能覆盖原有类的同名属性,因为扩展属性是在该类之外定义的。 |
总之,虽然 Kotlin 的扩展属性能够为现有的类添加新的属性,但是我们需要注意其限制,避免滥用扩展属性导致代码混乱和不可维护。