Kotlin中的扩展函数和扩展属性

发布时间 2023-05-30 20:40:15作者: jqc

扩展函数

Kotlin中可以给一个类额外添加这个类中没有的函数,即扩展函数。例如:

fun Int.dp2px(context: Context) {
	TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics)
}

fun test () {
	println(16.dp2px())
}

上述代码给Int类添加了一个扩展函数dp2px用于将dp转成px,在代码中可以直接对Int类型的变量调用dp2px函数,就好像Int类的其他固有函数一样。在扩展函数中可以通过this访问调用扩展函数的变量本身。

需要注意的是,扩展函数的调用者类型是静态声明的类型,而不是实际的运行时类型,例如:

fun Context.toString() : String {
	return "context"
}
fun Activity.toString() : String {
	return "activity"
}

fun printContext(context: Context) {
	println(context.toString())
}

fun test(activity: Activity) {
	printContext(activity)
}

上述代码调用test函数输出的结果是"context"而不是"activity"

扩展属性

Kotlin中还可以给一个类额外添加这个类中没有的属性,即扩展属性。例如:

val View.activity : Activity?
	get() = this.context as? Activity

上述代码给View类添加了一个扩展属性activity,扩展属性并不是真的给该类添加了一个属性,所以只能通过gettersetter来访问和初始化

扩展函数和扩展属性的原理

class Test {

    fun Int.dp2px(context: Context) {
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics)
    }

    val View.activity : Activity?
        get() = this.context as? Activity

}

通过将上述Kotlin代码转成字节码再反编译后如下:

public final class Test {

   public final void dp2px(int $this$dp2px, @NotNull Context context) {
      Intrinsics.checkNotNullParameter(context, "context");
      float var10001 = (float)$this$dp2px;
      Resources var10002 = context.getResources();
      Intrinsics.checkNotNullExpressionValue(var10002, "context.resources");
      TypedValue.applyDimension(1, var10001, var10002.getDisplayMetrics());
   }

   @Nullable
   public final Activity getActivity(@NotNull View $this$activity) {
      Intrinsics.checkNotNullParameter($this$activity, "$this$activity");
      Context var10000 = $this$activity.getContext();
      if (!(var10000 instanceof Activity)) {
         var10000 = null;
      }

      return (Activity)var10000;
   }

}

可以发现扩展函数和扩展属性的原理是在编译后生成了特定的函数,并将扩展类型的对象作为第一个参数传递进去。
对于Int类型的扩展函数dp2px实际是在Test类中生成一个dp2px函数,并接收Int类型的参数$this$dp2px作为第一个参数。
对于View类型的扩展属性activity实际是在Test类中生成一个getActivity函数,并接收View类型的参数$this$activity作为第一个参数