第6章 扩展函数与属性
在使用Java的时候,我们经常使用诸如StringUtil, DateUtil等等一堆工具类,代码写起来也比较冗长。举个例子,获取一个字符串的第一个字符值、最后一个字符值。如果我们用Java代码来写,通常是要先声明一个StringUtil类,然后在里面写相应的工具方法,代码可以是下面的这个样子
package com.easy.kotlin;
import static java.lang.System.out;
public class StringUtil {
/**
* 获取str的第一个字符值
*
* @param str
* @return
*/
public static String firstChar(String str) {
if (str != null && str.length() > 0) {
return str.charAt(0) + "";
}
return "";
}
/**
* 获取str的最后一个字符值
*
* @param str
* @return
*/
public static String lastChar(String str) {
if (str != null && str.length() > 0) {
return str.charAt(str.length() - 1) + "";
}
return "";
}
public static void main(String[] args) {
String str = "abc";
out.println(StringUtil.firstChar(str)); // a
out.println(StringUtil.lastChar(str)); // c
}
}
我们可以看到 StringUtil.firstChar(str) 这样的调用方式不够简单直接。能不能直接这样调用
"abc".firstChar()
"abc".lastChar()
非常遗憾的是,在Java中我们无法给String类添加一个自定义方法。因为String类是JDK中内置的基础类,而且为final,不能修改。所以,Java程序员通常使用这样的一个变通的方法:开发一个StringUtil类,在里面封装所需要的String操作的方法。而不是修改或继承String类。
而情况到了Kotlin里面,就完全不一样了——我们完全可以自由扩展任何类的方法和属性。在不修改原类的情况下, Kotlin能给一个类扩展新功能而无需继承该类。
本章我们介绍Kotlin的扩展函数和属性。
6.1 扩展函数
Kotlin中提供了非常简单使用的扩展函数功能。我们可以为现有的类自由添加自定义的函数 。
6.1.1 给String类扩展两个函数
例如,我们现在给String类扩展两个函数:lastChar() 和 firstChar() , 实现代码如下
package com.easy.kotlin
fun String.lastChar(): String {
if (this.length == 0) {
return ""
}
return this[this.length - 1].toString()
}
fun String.firstChar(): String {
if (this.length == 0) {
return ""
}
return this[0].toString()
}
扩展函数的语法,我们用下图来简单说明
然后,我们就可以在代码中直接调用了
fun main(args: Array<String>) {
val str = "abc"
println(str.firstChar()) // a
println(str.lastChar()) // c
}
如果在其他package路径下面,需要 import 导入扩展函数
package com.easy.kotlin.tutorial // 跟扩展函数不在同一个包路径下面
import com.easy.kotlin.firstChar // 导入扩展函数 firstChar
import com.easy.kotlin.lastChar// 导入扩展函数 lastChar
fun main(args: Array<String>) {
val str = "abc"
str.firstChar() // 这样的调用方式要比 StringUtil.firstChar(str) 简单优雅许多
str.lastChar()
}
6.1.2 给 List 类扩展一个过滤函数
在上一章中我们介绍过List的filter函数。那么这个 filter 函数是怎样实现的呢? 如果我们自己来给List类扩展一个过滤函数,应该怎样去做呢?下面我们就来介绍。
为了更加深刻体会到 Kotlin 扩展功能的简单优雅性,我们先来看看在Java中是怎样实现的吧!首先,我们会去声明一个ListUtil 类,里面实现一个 List<T> filter(List<T> list, Predicate<T> p) 方法,代码如下
public class ListUtil<T> {
/**
* 根据谓词p 过滤 list 中的元素
*
* @param list
* @param p
* @return
*/
public List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T t : list) {
if (p.predicate(t)) {
result.add(t);
}
}
return result;
}
}
其中,Predicate 接口声明如下
interface Predicate<T> {
Boolean predicate(T t);
}
然后,我们在代码中这样使用这个 filter 方法
public static void main(String[] args) {
List<Integer> list = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7});
ListUtil<Integer> listUtil = new ListUtil(); // 声明ListUtil对象
List<Integer> result = listUtil.filter(list, (it) -> it % 2 == 1); // lambda 表达式
out.println(result); // [1, 3, 5, 7]
}
为了调用 filter 方法,我们还要声明一个ListUtil对象,费事。能不能直接像下面这样调用?
list.filter { it % 2 == 1 }
答案是肯定的,只不过是在Kotlin中,而不是在Java中。
我们就来使用Kotlin的扩展函数为List扩展一个filter函数,代码如下
fun <T> List<T>.filter(predicate: (T) -> Boolean): MutableList<T> {
val result = ArrayList<T>()
this.forEach {
if (predicate(it)) {
result.add(it)
}
}
return result
}
这个函数的签名稍微有点复杂,我们用下图来形象化地简单说明
然后,我们在代码中只需要这样调用即可
val list = mutableListOf(1, 2, 3, 4, 5, 6, 7)
val result = list.filter {
it % 2 == 1
}
println(result) // [1, 3, 5, 7]
Kotlin的标准库API中使用扩展的功能,通过扩展Java的API,提供了大量的实用简单的函数。我们将在后面的关于文件IO、正则表达式、多线程等相关主题中介绍。
6.2 扩展属性
除了扩展一个类的函数,我们还可以扩展类属性。例如,我们给 MutableList 扩展两个属性:firstElement 和 lastElement , 实现代码如下
var <T> MutableList<T>.firstElement: T
get() {
return this[0]
}
set(value) {
this[0] = value
}
var <T> MutableList<T>.lastElement: T
get() {
return this[this.size - 1]
}
set(value) {
this[this.size - 1] = value
}
上面的代码中的扩展属性的语法说明如下图所示
然后,我们就可以在代码中直接使用扩展的属性了
val list = mutableListOf(1, 2, 3, 4, 5, 6, 7)
println("list = ${list}") // list = [1, 2, 3, 4, 5, 6, 7]
println(list.firstElement) // 调用getter 函数 , 值是 1
println(list.lastElement) // 7
list.firstElement = -1 // 调用 setter 函数
list.lastElement = -7
println("list = ${list}") // list = [-1, 2, 3, 4, 5, 6, -7]
println(list.firstElement) // -1
println(list.lastElement) // -7
扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。
6.3 扩展中的this关键字
我们有注意到,在上面的List的扩展函数filter的实现中,我们有用到一个this关键字
this.forEach {
if (predicate(it)) {
result.add(it)
}
}
这里的 this 指的是 接收者对象(receiver object), 也就是调用扩展函数时, 在点号( . )之前指定的对象实例。
本章小结
扩展函数是Kotin非常方便实用的功能,使用扩展函数,我们的代码写起来也更加简单优雅。同时也正是通过这个特性,Kotlin 在Java API的基础上扩展了丰富实用的函数,我们将在后面的章节中看到。