第6章 扩展函数与属性

简介: 第6章 扩展函数与属性在使用Java的时候,我们经常使用诸如StringUtil, DateUtil等等一堆工具类,代码写起来也比较冗长。举个例子,获取一个字符串的第一个字符值、最后一个字符值。

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

这个函数的签名稍微有点复杂,我们用下图来形象化地简单说明

给List扩展的filter函数签名

然后,我们在代码中只需要这样调用即可

    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的基础上扩展了丰富实用的函数,我们将在后面的章节中看到。

相关文章
|
3月前
|
缓存 Java 数据库连接
扩展类的附加特性
扩展类的附加特性
27 0
|
6月前
|
存储 程序员 Swift
Swift开发——存储属性与计算属性
Swift推荐使用结构体进行开发,结构体支持属性和方法,且作为值类型。结构体属性包括存储属性(如radius)和计算属性(如r),计算属性不存储值,类似方法。结构体用`struct`定义,命名遵循大驼峰规则。实例名遵循小驼峰规则。属性可在结构体中任意位置定义,静态属性用`static`。存储属性可为`lazy`实现懒加载。结构体实例通过`.`访问属性和方法,静态属性和方法用`结构体名.`访问。计算属性可读写,可通过`get`和`set`定义。程序段1展示了结构体Point和Circle的属性和方法,包括私有属性、只读计算属性、可读写计算属性及`mutating`方法。
48 0
Swift开发——存储属性与计算属性
|
JavaScript 前端开发
计算属性和监视属性的区别
计算属性和监视属性的区别
|
7月前
|
开发框架 .NET C#
C# 10.0中的扩展属性与模式匹配:深入解析
【1月更文挑战第20天】C# 10.0引入了众多新特性,其中扩展属性与模式匹配的结合为开发者提供了更强大、更灵活的类型检查和代码分支能力。通过这一特性,开发者可以在不修改原始类的情况下,为其添加新的行为,并在模式匹配中利用这些扩展属性进行更精细的控制。本文将详细探讨C# 10.0中扩展属性与模式匹配的工作原理、使用场景以及最佳实践,帮助读者更好地理解和应用这一新功能。
|
7月前
|
JavaScript 前端开发 编译器
TypeScript【可选属性、只读属性、额外的属性检查、函数类型、类类型、继承接口】(四)-全面详解(学习总结---从入门到深化)
TypeScript【可选属性、只读属性、额外的属性检查、函数类型、类类型、继承接口】(四)-全面详解(学习总结---从入门到深化)
79 0
|
JavaScript 索引
数组的扩展和新增方法
数组的扩展和新增方法
101 0
C++函数适配器和函数包装器:让你的函数更灵活
前言:这次给大家带来的是C++函数适配器和函数包装器,它可以让你的让你的函数更灵活,更加易用
|
移动开发 程序员
H5 中 bordercolorlight 属性的用法及作用
H5 中 bordercolorlight 属性的用法及作用
246 0
H5 中 bordercolorlight 属性的用法及作用
lodash创建一个函数属性名称的数组,包含继承属性
lodash创建一个函数属性名称的数组,包含继承属性
97 0