kotlin支持在不修改原有类的情况下增加新的函数,增加的函数就像原本类中就有这个函数一样。
扩展函数
例如之前有一个Person类,可以通过getName()获取中文名,现在想增加一个获取英文名称的函数,就通过扩展函数实现。可以在不修改原有类的情况下,增加功能。
kotlin
class Person {
fun getName(): String {
return "张三"
}
}
扩展函数的写法如下:
kotlin
/**
* fun 类名.函数名(){
*
* }
*/
fun Person.getEnglishName(): String {
return "ZhangSan"
}
通过 类名.函数名就为该类添加了一个扩展函数。 这个类就多了一个函数,扩展函数的调用方式和普通函数一样。实例对象.函数()就行了。
kotlin
class Person {
fun getName(): String {
return "张三"
}
}
/**
* fun 类名.函数名(){
*
* }
*/
fun Person.getEnglishName(): String {
return "ZhangSan"
}
fun main() {
val person = Person()
person.getName()
person.getEnglishName()
}
扩展函数的实现原理
将上面的kotlin代码反编译java代码,可以看到kotlin的扩展函数,在java层面实际上就是一个static函数,只不过这个函数接收了一个实例对象。扩展函数本质上是通过把实例对象传入到函数里面,来实现对类功能的扩展。
扩展函数需要接收一个实例,所以kotlin被扩展的类,也就是要作为参数传入实例称为
receiver。
类名.函数名()中的类名就叫receiver
java
public final class Person {
public final String getName() {
return "张三";
}
}
public final class PersonKt {
// 扩展函数对应的java方法是一个static方法
// 将Parent作为参数传进来
public static final String getEnglishName(@NotNull Person $this$getEnglishName) {
Intrinsics.checkNotNullParameter($this$getEnglishName, "<this>");
return "ZhangSan";
}
public static final void main() {
Person person = new Person();
person.getName();
// 调用static方法,然后把person传入其中
getEnglishName(person);
}
public static void main(String[] args) {
main();
}
}
上面的扩展函数都是写在类外面的,也是就顶级函数,直接定义在文件顶层,不属于任何类。扩展函数其实也可以写在类里面,如下:
kotlin
class Person {
fun getName(): String {
return "张三"
}
fun Person.getEnglishName2(): String {
return "ZhangSan_V2"
}
}
写在了类里面就没法像调用成员函数一样调用了,没办法像调用getEnglishName一样调用getEnglishName2。

因为getEnglishName2就不是一个static方法了,在java层面就是一个成员函数。反编译的java代码如下:
java
public final class Person {
public final String getName() {
return "张三";
}
// 非static方法
public final String getEnglishName2(@NotNull Person $this$getEnglishName2) {
Intrinsics.checkNotNullParameter($this$getEnglishName2, "<this>");
return "ZhangSan_V2";
}
}
按照反编译的java代码来看,想要调用getEnglishName2就需要先创建Person的实例对象,再把用这个实例调用getEnglishName2并作为参数传入。

但是很可惜kotlin不支持这种写法。
在有T.()或者Person.()作为入参的方法中才能调用。


因为apply是一个inline函数,inline函数在编译时会在编译时把代码直接插入到调用的地方,所以反编译的java代码,没有apply方法里面的东西,看不出来T.()是什么。因此复制apply的代码重新写一个apply2,只删除inline关键字。代码如下:
kotlin
// kotlin源码
fun main() {
val person = Person()
person.getName()
person.getEnglishName()
person.apply2 {
getEnglishName2()
}
}
fun <T> T.apply2(block: T.() -> Unit): T {
block()
return this
}
// 反编译的java代码
public final class PersonKt {
public static final void main() {
Person person = new Person();
person.getName();
getEnglishName(person);
apply2(person, PersonKt::main$lambda$0);
}
public static final Object apply2(Object object, @NotNull Function1 block) {
// 间接调用main$lambda$0()并传入person实例
block.invoke(object);
return object;
}
private static final Unit main$lambda$0(Person person) {
// 调用扩展函数
person.getEnglishName2(person);
return Unit.INSTANCE;
}
}
从反编译的代码可以看出,T.() -> Unit对应的Function1类。T.()就是Function1.invoke()的入参。所以kotlin也把T.()中的T称为Receiver。
kotlin通过Function1来实现了扩展函数的调用。
kotlin
public interface Function1<in P1, out R> : Function<R> {
public operator fun invoke(p1: P1): R
}
小结:在类中定义的扩展函数,需要在该类的扩展函数的实现中才能调用。例如Person类中定义的扩展函数,就需要在 **Person.() -> Any**的实现中调用。
扩展函数本质上就是将receiver作为参数传到函数中。Persson.getEnglishName()本质上就是getEnglishName(persion)。
实际案例
Compose中的Modifier很多地方都用到了写在类中的扩展函数。
kotlin
interface ColumnScope {
// 定义在接口中的扩展函数
// 对应的java代码是 weight(modifier,weight,fill)
fun Modifier.weight(
weight: Float,
fill: Boolean = true
): Modifier
}
interface Modifier {
companion object : Modifier {
override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial
override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial
override fun any(predicate: (Element) -> Boolean): Boolean = false
override fun all(predicate: (Element) -> Boolean): Boolean = true
override infix fun then(other: Modifier): Modifier = other
override fun toString() = "Modifier"
}
}
weight定义在了ColumnScope接口中,想要调用weight就需要先创建一个ColumnScope的实例,再把一个Modifier的实例传入到weight(moidifier)。
所以只有在ColumnScope.() -> Unit的实现中才能调用到weight()。在外部调用就会找不到weight函数,因为外部没有ColumnScope的实例。

kotlin
Column() {
/**
* 对应的java代码:
* columnScope.weight(Modifier,1f)
*/
Modifier.weight(1f)
}
扩展属性
除了函数可以扩展,属性也可以扩展。
扩展属性写法:Val 类名.属性名
kotlin
/**
* 扩展属性
*/
val Person.chineseName: String
get() = "张三"
/**
* 扩展函数
*/
fun Person.getEnglishName(): String {
return "ZhangSan"
}
调用方式和普通的成员变量一样。如果是val只读变量,扩展属性本质上和扩展函数在java层面没有任何区别,都是生成了一个static方法。
kotlin
public final class PersonKt {
// 扩展属性
// 对应kotlin的 val Person.chineseName
public static final String getChineseName(@NotNull Person $this$chineseName) {
Intrinsics.checkNotNullParameter($this$chineseName, "<this>");
return "张三";
}
// 扩展函数
// 对应 fun Person.getEnglishName()
public static final String getEnglishName(@NotNull Person $this$getEnglishName) {
Intrinsics.checkNotNullParameter($this$getEnglishName, "<this>");
return "ZhangSan";
}
}
var可变属性会多生成一个set方法。
kotlin
public final class PersonKt {
// var Person.chineseName
public static final String getChineseName(@NotNull Person $this$chineseName) {
Intrinsics.checkNotNullParameter($this$chineseName, "<this>");
return "张三";
}
// var Person.chineseName
public static final void setChineseName(@NotNull Person $this$chineseName, @NotNull String value) {
Intrinsics.checkNotNullParameter($this$chineseName, "<this>");
Intrinsics.checkNotNullParameter(value, "value");
}
}
和扩展函数一样,扩展属性除了能写在类外面,还可以写在类里面。在java代码层面,扩展属性和扩展函数的基本额没有区别。