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<out R> : Function<R> {
    /** Invokes the function. */
    public operator fun invoke(): R
}
....
....
/** A function that takes 22 arguments. */
public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> {
    /** 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<Integer, Unit>() {
            @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文件中,通过生成的类来访问静态方法。

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容