Kotlin函数关键字inline内联函数原理

2022-09-27  本文已影响0人  ModestStorm

前言

Kotlin扩展函数为我们提供了方便的开发调用,不用通过继承或者装饰者模式来实现功能的扩展,它的内部原理其实是通过封装了public static final method(调用者对象作为参数)方法,效率上并没有什么提升。

扩展函数定义

1.在ExtentKotlin.kt中定义扩展函数:

 fun String.printlnHaHa(){
    var result = "haha$this"
    println(result)
}

2.外部使用扩展函数:

open class MediaPlayer(var path:String) {
     fun play(path:String){
        "aa".printlnHaHa()
    }
}

3.查看字节码生成,反编译为java类
AS -> tools -> Kotlin -> show Kotlin byte code -> Decompile

4.ExtentKotlin.kt中的扩展函数反编译

public final class ExtentKotlinKt {
   public static final void printlnHaHa(@NotNull String $this$printlnHaHa) {
      Intrinsics.checkParameterIsNotNull($this$printlnHaHa, "$this$printlnHaHa");
      String result = "haha" + $this$printlnHaHa;
      System.out.println(result);
   }
}

可以看到扩展函数语法糖背后的实现原理
1.生成了一个final类ExtentKotlinKt
2.将扩展函数转变为了public static final 类型的方法,方法参数是类扩展函数类型的参数,方法内部使用这个函数做了操作处理
3.根据分析从而得知其他地方就是通过调用ExtentKotlinKt的static final方法实现的调用。

  1. 扩展函数的外部调用
public class MediaPlayer {
   @NotNull
   private String path;

   public final void play(@NotNull String path) {
      Intrinsics.checkParameterIsNotNull(path, "path");
      // 在这里调用了外部扩展类的静态方法,并将调用对象传递给扩展函数了
      ExtentKotlinKt.printlnHaHa("aa");
   }

   @NotNull
   public final String getPath() {
      return this.path;
   }

   public final void setPath(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.path = var1;
   }

   public MediaPlayer(@NotNull String path) {
      Intrinsics.checkParameterIsNotNull(path, "path");
      super();
      this.path = path;
   }
}

inline关键字原理

还以上面的例子分析扩展函数,在扩展函数加上inline关键字进行修饰,查看反编译后的效果

1.inline关键字修饰扩展函数

 inline fun String.printlnHaHa(){
    var result = "haha$this"
    println(result)
}

2.查看反编译后对应的java文件

public final class ExtentKotlinKt {
   public static final void printlnHaHa(@NotNull String $this$printlnHaHa) {
      int $i$f$printlnHaHa = 0;
      Intrinsics.checkParameterIsNotNull($this$printlnHaHa, "$this$printlnHaHa");
      String result = "haha" + $this$printlnHaHa;
      System.out.println(result);
   }
}

与扩展函数的实现一样并没有什么改变,还是public static final方法

3.外部类使用inline关键字的扩展函数

open class MediaPlayer(var path:String) {
     fun play(path:String){
        "aa".printlnHaHa()
    }
}

4.反编译外部类

public class MediaPlayer {
   @NotNull
   private String path;

   public final void play(@NotNull String path) {
      Intrinsics.checkParameterIsNotNull(path, "path");
    /*
    这里的调用发生了改变,不再是直接调用
    ExtentKotlinKt.printlnHaHa("aa"), 
    而是直接将方法体中的内容复制了一遍执行,
    这么做可以减少方法的查找和调用,效率上有所提高了
    */
      String $this$printlnHaHa$iv = "aa";
      int $i$f$printlnHaHa = false;
      String result$iv = "haha" + $this$printlnHaHa$iv;
      System.out.println(result$iv);
   }

   @NotNull
   public final String getPath() {
      return this.path;
   }

   public final void setPath(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.path = var1;
   }

   public MediaPlayer(@NotNull String path) {
      Intrinsics.checkParameterIsNotNull(path, "path");
      super();
      this.path = path;
   }

总结

inline内联函数会复制一遍函数内部方法体的实现,这样会让使用处的代码膨胀. 所以官方极力建议, 内联函数的代码不建议超过 3 行. 控制在 1~3 行代码是最佳的。

上一篇下一篇

猜你喜欢

热点阅读