kotlin语法糖实现

对于kotlin和java,两个语言都是在jvm上运行的,所以它们最终生产的字节码将会是一致的。对于kotlin的一些比较方便的语法糖,我们可以利用其等同的java代码,来验证其内部的实现。

字节码和.kt对应的.java文件

通过AndroidStudio的tool -> kotlin -> show kotlin bytecode,可以生产kt文件对应的字节码,然后在字节码文件上,选中Decompile,反编译字节码文件,可以生成对应的.java文件。

可空参数与非空参数

kotlin的参数定义时,必须标明是否可空。

 var callBack: String? = null
 var currentFlow:PayFlow = PayFlow.Idle

第一个参数表示可空的,第二个参数表示非空参数
其实在java中,就是对应以下的定义

@Nullable
private static String callBack;
@NotNull
private static final ReadWriteProperty currentFlow$delegate

其实kotlin实现非空参数,跟java通过注解@Nullable和@NotNull来实现是一样的。

object关键字来实现单例

object PayFlowManager {
}

对应的java实现为

public static final PayFlowManager INSTANCE;
static {
      PayFlowManager var0 = new PayFlowManager();
      INSTANCE = var0;
}

利用了static加载的方式,来创建单例。也就是我们常说的单例的饿汉实现。同时,这个创建出来的变量名为INSTANCE,所以我们java调用的方式都需要通过类名.INSTANCE来访问kt的单例。

lambda

kotlin中,支持传入lambda作为成员变量

var succeedViewAction: () -> Unit = {}

这个lambda表示当前接收空参数,并且返回值为空。在java中的表示为

 @NotNull
 private Function1 payResultAction;

这个Function1是一个接口,也就是说,kt的lambda对于java来说,就是一个接口。lambda每多一个参数,都有一个相对应的Function,后面的数字就表示参数的个数。

** A function that takes 0 arguments. */
public interface Function0 : Function {
    /** Invokes the function. */
    public operator fun invoke(): R
}
....
....
/** A function that takes 22 arguments. */
public interface Function22 : Function {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
}

也就是说,目前kt的最多支持的lambda的参数的个数是22个。
所以,在java中传递这个lambda,就是set这个接口的实现类,也就是匿名内部类。

payTypeParam.setPayResultAction(new Function1() {
            @Override
            public Unit invoke(Integer integer) {
                ...
                return null;
            }
        });

companion object

在kt中,通常我们用companion object来作为静态成员的存储。

companion object {
       val PAY_SOURCE_NONE = 0
       val PAY_SOURCE_LIST = 1
       val PAY_SOURCE_ACTIVITY = 2
       val PAY_SOURCE_COMIC = 3
       val PAY_SOURCE_NOTICE = 4
       val PAY_SOURCE_LIVE = 5
       val PAY_SOURCE_VIP_CENTER = 6
       val PAY_SOURCE_PARTNER = 8
       val PAY_SOURCE_COMIC_LAYER = 9
   }

对应的java代码为:

public interface PaySource {
   PaySource.Companion Companion = PaySource.Companion.$$INSTANCE;
public static final class Companion {
      private static final int PAY_SOURCE_NONE = 0;
      private static final int PAY_SOURCE_LIST = 1;
      private static final int PAY_SOURCE_ACTIVITY = 2;
      private static final int PAY_SOURCE_COMIC = 3;
      private static final int PAY_SOURCE_NOTICE = 4;
      private static final int PAY_SOURCE_LIVE = 5;
      private static final int PAY_SOURCE_VIP_CENTER = 6;
      private static final int PAY_SOURCE_PARTNER = 8;
      private static final int PAY_SOURCE_COMIC_LAYER = 9;
      // $FF: synthetic field
      static final PaySource.Companion $$INSTANCE;
      static {
         PaySource.Companion var0 = new PaySource.Companion();
         $$INSTANCE = var0;
    }
    ...
    等对应的get和set方法
    }
}

也就是说,内部会生成一个Companion的静态内部类,同时生成一个这个静态内部类对应的实例。java调用时,将会直接调用这个Companion变量来访问静态方法和静态变量。

kotlin Extension

kt提供了一个插件,让我们可以在用到xml的地方直接用id进行访问,而不用再通过findViewById来初始化View,我们需要先导入对应的生成文件

import kotlinx.android.synthetic.main.activity_vip_recharge.*

在对应java实现中,我们可以知道其内部的实现

public View _$_findCachedViewById(int var1) {
      if (this._$_findViewCache == null) {
         this._$_findViewCache = new HashMap();
      }

      View var2 = (View)this._$_findViewCache.get(var1);
      if (var2 == null) {
         var2 = this.findViewById(var1);
         this._$_findViewCache.put(var1, var2);
      }

      return var2;
   }

   public void _$_clearFindViewByIdCache() {
      if (this._$_findViewCache != null) {
         this._$_findViewCache.clear();
      }

   }

对于每一个通过id直接调用view的类,都会生成_$_findCachedViewById方法,在这个方法中,提供了findViewById的操作,同时会创建出一个HashMap作为缓存时,防止多次使用时重复的执行findViewById操作。

 ((ImageView)this._$_findCachedViewById(id.icBack)).setOnClickListener((OnClickListener)this);
      ((TextView)this._$_findCachedViewById(id.tradingRecord)).setOnClickListener((OnClickListener)this);
      ((KKLayoutButton)this._$_findCachedViewById(id.btnAction)).setOnClickListener((OnClickListener)this);
      ((TextView)this._$_findCachedViewById(id.autoContinue)).setOnClickListener((OnClickListener)this);

kt在每一个使用到id的地方,都将入侵式的替换代码,通过我们给定的id来调用findViewById。

lateinit关键字

对于一个我们保证肯定会在生命周期一开始就初始化的值,我们可以用lateinit来修饰

private lateinit var mMemberAutoContinueClose: ImageView

那么kt如何保证这个lateinit的有效性呢?

if (this.mMemberAutoContinueClose == null) {
         Intrinsics.throwUninitializedPropertyAccessException("mMemberAutoContinueClose");
      }

在每一次使用这个属性之前都会先做一次判空操作,一旦为空,将会直接抛出异常。

扩展函数

kt的一个非常牛逼的功能,其实就是扩展函数,我们可以扩展一些基础类的基本功能,比如说我们给Context扩展toast功能:

fun Context?.toast(@StringRes toastRes: Int) {
    if (this == null) {
        return
    }
    val text = getString(toastRes)
    toast(text)
}

对应的生成java代码:

public static final void toast(@Nullable Context $receiver, @StringRes int toastRes) {
      if ($receiver != null) {
         String text = $receiver.getString(toastRes);
         Intrinsics.checkExpressionValueIsNotNull(text, "text");
         toast($receiver, text);
      }
   }

也就是说,扩展函数的功能其实就是基于静态方法来实现的。被扩展类作为第一个入参,后续的参数就是扩展函数需要的参数。
而且,每一个扩展函数的文件,都会生成对应名称的class文件。比如说KotlinExt.kt将会生成KotlinExtKt这个类。最后java文件中,通过生成的类来访问静态方法。

你可能感兴趣的:(kotlin语法糖实现)