对于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文件中,通过生成的类来访问静态方法。