Kotlin 特性之扩展函数

什么是扩展函数

扩展函数是 Java 不具备的,而 Kotlin 独有的特性,在日常开发中使用频率很高,类似于设计模式中的装饰模式,其作用就是在不改变原有类的情况下,扩展新的功能。

如何使用扩展函数和扩展属性

扩展函数的基本使用

在需要被扩展的类或者接口后面添加方法即可,下面是一个给 Int 类型扩展了一个判断是否是偶数的方法的例子,在扩展函数里使用 this 指代被扩展的类或者接口的实例对象:

/**
 * 是否是偶数,用与运算取出最后一位,最后一位为0则是偶数
 */
fun Int.isEven(): Boolean {
    return (this and 1) == 0
}

使用的时候,对于任意一个 Int 型数据都可以直接调用 isEven() 方法。

2.isEven() //得到返回值true
扩展属性的基本使用

扩展属性实际上是提供一种方法来访问属性而已,并且这些扩展属性是没有任何的状态的,因为不可能给被扩展的类或者接口的对象额外添加属性字段,只是使用简洁语法类似直接操作属性,实际上还是方法的访问。例如给 TextView 扩展一个属性表示是否是粗体:

//扩展属性定义
var TextView.isBolder: Boolean
	get() {
		return this.paint.isFakeBoldText
	}
	set(value) {
		this.paint.isFakeBoldText = value
	}

在扩展属性中也是使用 this 指代被扩展的类或者接口的实例对象。另外必须定义 get() 方法,在 Kotlin 中类中的属性都是默认添加 get() 方法的,但是由于扩展属性并不是给现有类添加额外的属性,自然就没有默认 get() 方法实现了,所以必须手动添加 get() 方法。至于 set() 方法,就看属性是否可变了,也就是该扩展属性对应是 var 还是 val 了。

什么是顶层函数

在解析扩展函数的本质之前,先来了解一下 Kotlin 的另一个特性 – 顶层函数。

在 Java 中有静态方法和静态属性,一般是为了提供全局共享访问的方法和属性,是独立于对象之外的。静态方法和静态属性需要在类中声明,在使用的时候也是通过 类名.方法名 或者 类名.属性名 的方式访问。静态函数内部是不包含状态的,也就是所谓的纯函数,它的输入仅仅来自于它的参数列表,而它的输出也仅仅依赖于它参数列表,静态函数所在的类只是作为一个容器的角色。

在 Kotlin 中认为一个函数有时候并不需要属于任何一个类,它可以独立存在。所以在 Kotlin 中类似静态函数和静态属性可以去掉外层类的容器,一个函数或者属性可以直接定义在一个 Kotlin 文件的顶层中,在使用的地方只需要 import 这个函数或属性即可,这就是顶层函数。

顶层函数的使用

首先,创建一个文件,这里文件取名为 TopFunction:
创建文件

然后在文件中定义函数,这里定义了一个判断 Int 数字是否是偶数的函数:

/**
 * 是否是偶数,用与运算取出最后一位,最后一位为0则是偶数
 */
fun isEven(num: Int): Boolean {
    return (num and 1) == 0
}

需要使用的时候直接调用函数即可:

isEven(2) //得到返回值true

定义并使用一个顶层函数很简单,但是顶层函数究竟是怎么运行在 JVM 中的呢?如果是 Java 和 Kotlin 混合开发模式,在 Kotlin 中定义的顶层函数,在 Java 中又是怎么调用的呢?

顶层函数的本质

通过 decompile 看下反编译后对应的 Java 代码:

public final class TopFunctionKt {
   public static final boolean isEven(int num) {
      return (num & 1) == 0;
   }
}
  • 顶层文件会反编译成一个容器类。(类名默认为顶层文件名+"Kt"后缀)
  • 顶层函数会反编译成一个 static 静态函数。

所以顶层函数本质就是 Java 中的静态函数。如果需要在 Java 中调用 Kotlin 中的顶层函数,方式很简单,就是利用反编译生成的类作为静态函数容器类直接调用对应的函数,例如上面的顶层函数在 Java 中调用写法,TopFunctionKt.isEven(int num)。

注意: 顶层文件反编译成的 Java 中的容器类名默认是顶层文件名+“Kt”后缀,但是也是可以自定义的。也就是说顶层文件名和生成容器类名没有必然的联系。通过 Kotlin 中的 @file: JvmName(“自定义生成类名”) 注解就可以自动生成对应 Java 调用类名,注意需要放在文件顶部,在package声明的前面。这样 Java 调用自定义类名顶层函数就更加自然,一般建议使用注解修改类名。

扩展函数的本质

扩展函数也是不属于任何一个类,独立存在于 Kotlin 文件中,是不是和顶层函数一样,本质是 Java 中的静态函数呢?答案是肯定的。

/**
 * 是否是偶数,用与运算取出最后一位,最后一位为0则是偶数
 */
fun Int.isEven(): Boolean {
    return (this and 1) == 0
}

我们将上述扩展函数写在文件名为 SpreadFunction 的文件中,通过 decompile 看下反编译后对应的 Java 代码:

public final class SpreadFunctionKt {
   public static final boolean isEven(int $this$isEven) {
      return ($this$isEven & 1) == 0;
   }
}

跟顶层函数如出一辙,扩展函数本质就是 Java 中的静态函数,被扩展的类或者接口的实例对象会作为函数参数,用 this 指代。同样的,在 Java 中调用 Kotlin 中的扩展函数也是一样的方式,不同的地方是需要插入一个被扩展的类或者接口的实例对象。

扩展属性实际上就是提供某个属性访问的set,get方法,set,get方法本质都是静态函数,同时都会传入一个被扩展类的对象,然后在其内部用这个实例对象去访问和修改对象所对应的类的属性,在此就不细说了。

扩展函数与成员函数

  1. 扩展函数和成员函数都可以访问被扩展类的公有方法和属性。
  2. 扩展函数不能访问被扩展类的私有方法和属性,成员函数可以访问类中的私有方法和属性。
  3. 父类成员函数可以被子类重写,但扩展函数不可以被子类重写。

理解了扩展函数的本质是一个静态函数,且处于类的外部,被扩展类的实例对象作为函数参数传入,就可以很好地理解扩展函数与成员函数之间的共同性和差异性了。扩展函数通过实例对象进行访问自然不能访问类中的私有方法和属性,但可以访问类中的公有方法和属性。扩展函数处于类的外部,并不是类的一部分,不会被子类所继承,自然不可以被子类重写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值