专栏首页一个会写诗的程序员的博客第6章 扩展函数与属性第6章 扩展函数与属性

第6章 扩展函数与属性第6章 扩展函数与属性

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Kotlin 扩展函数与属性 实例代码

    一个会写诗的程序员
  • 第5章 函数与函数式编程第5章 函数与函数式编程

    函数式编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以传入函数参数,也可以返回一个函数。函数式编程 (简称FP) 是一种编程...

    一个会写诗的程序员
  • 可逆加密解密 Java 代码实例

    一个会写诗的程序员
  • Android webview如何加载HTML,CSS等语言的示例

    这个题目可能取得不大好,想了很久没想出更合适的了。在android开发webview的时候,有的时候后台不一定给的就是一个url,而是把一些HTML,css,j...

    砸漏
  • 剑指offer No.43 左旋转字符串

    汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输...

    week
  • Shiro系列 | 《Shiro开发详细教程》第五章:Shiro编码加密

    在涉及到密码存储问题上,应该加密或者生成密码摘要存储,而不是存储明文密码。为避免数据泄露对用户造成很大的损失,应该加密或者生成不可逆的摘要方式存储。

    码神联盟
  • Java类库之正则表达式(重点)

    但是现在一个简单的问题就出现了,这个验证应该算是不难的,但是面对这样一个不麻烦的验证,代码写了9行代码,如果是一些更为复杂的验证呢?那么对于整体操作就更加的麻烦...

    葆宁
  • JVM各区溢出分析

    由于在Hotspot虚拟机中中不区分虚拟机栈和本地方法栈,因此通过-Xoss修改参数是无效的,可以通过修改-Xss设定。

    java乐园
  • Kotlin 扩展函数与属性 实例代码

    一个会写诗的程序员
  • Lambda什么时候使用+简化匿名内部类

扫码关注云+社区

领取腾讯云代金券