Kotlin 函数和扩展(extension)

324 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

前言

本文巩固一下Kotlin 函数扩展知识点。

本文大纲

Kotlin函数扩展.png

1. 回顾 Java 中 Utils 工具类

Utils 是无构造参数的 static 方法集合,主要用于扩展某个对象的功能,如 MathUtils,StringUtils, LogUtils。

Utils 类在一定程度上减少了重复代码的问题,是成本最低的 DRY(Don't repeat yourself)实践。

从代码设计的角度看,Utils 方法是 static 的,没有 OOP 的继承,重写,抽象的特性(static 本身就是反 OOP 的)。且 Utils 违反了单一职责,一个类应该包含其属性和所有操作方法。而 Utils 实现的方法并不在这个类内。

从使用的角度,必须预先知道 Utils 工具类的存在,能使用为这个类添加的扩展方法。 但在实际使用中,这个条件往往是缺失的,在团队开发中,个人无法掌握所有代码,因为不知道这个代码已经有人实现过了,导致都实现了自己的 Utils。所以会导致一个工程里同一个类的 Utils 往往会有好几个。

但存在必然是合理的。从开发者个人角度来看,使用 Utils 而不是对象继承的原因,主要是因为:

  1. 无法继承/重写这些类及其方法,只能通过 Utils 扩展;
  2. 继承一个类比抽取代码块封装为函数的实现成本+替换成本高;
  3. Utils 绝大情况下只是用来存储代码块,需求非常稳定,无需面向对象。

使用 Utils 的场景里很少会用到面向对象的特性,那么没有面向对象的缺点也并没有那么严重了。抛开 Utils 的设计缺点,是否可以避免使用上的缺点?Kotlin 提供的解决方法就是扩展(extension)。

2. Kotlin 扩展的使用和实现分析

声明一个 Kotlin 扩展如下:

// StringUtils.kt
fun String.appendHaha(): String {
    return this + "haha"
}

与普通的方法声明很相似,方法名前多了一个类名,表示其归属的类。扩展声明为顶层声明的时候可以被外部调用(因为函数是一等公民,在方法内部也可以声明扩展方法)。

函数体内用 this 来引用调用的实例,属性和方法的访问权限与普通调用一致。一致的意思是和正常在其他方法内部调用的权限一致,并不会因为是扩展声明就可以访问 private/propect 权限的属性和方法。这是因为扩展声明在字节码层面上其实是 static 方法。下面是appendHaha 对应 jvm 字节码的反编译结果:

public class StringUtilsKt {
    @NotNull
    public static final String haha(@NotNull String $this$haha) {
        Intrinsics.checkParameterIsNotNull($this$haha, "$this$haha");
        return $this$haha + "haha";
    }
}

所以从 Java 的角度来看,Kotlin 的扩展方法和 Utils 的调用没有差别,都是调用类的 static 方法然后传入操作的参数。实际上 Java 想要调用 Kotlin 的扩展方法也确实是这样调用的。

扩展方法的调用和实例方法调用一致,在调用者角度没有区别。

3. 总结

Kotlin 扩展依然没有解决 Utils 类的设计缺点。就像Kotlin Int 对 Java int,Kotlin property 对 Java field 一样,Kotlin 扩展是 对 Java 不完全面向对象的“清理”,使 Kotlin 更接近完全面向对象。相比 Utils 工具类,Kotlin 扩展特性能有效减少开发者心智负担和沟通成本,从而提高开发效率。

掘金(juejin) 一起分享知识,Keep Learning!