Kotlin编程JVM · Java虚拟机原理 · JVM上语言·框架· 生态系统

Kotlin Internals

2019-06-20  本文已影响144人  Kenber

Understanding what's under the hood of Kotlin

😃 优点

🎯 目标

  1. Kotlin优秀特性的内部实现原理、与Java差异、性能开销等

  2. 如何使用工具探明目标1

📝 工具

可空类型Int?自动装箱:

反编译字节码验证:

💪 Kotlin特性及实现原理

private Integer lazyInt;

public final int getLazyInt() {
    if (this.lazyInt == null) {
        this.lazyInt = 666;
    }
    return lazyInt;
}
private lateinit var lazyInt: Int

声明var为lateinit,让编译器在编译检查时不会因为属性变量未被初始化而报错,后续手动赋值初始化

val lazyInt by lazy { 666 }

声明val(一旦被(延迟)初始化不允许再被修改)为by lazy,后续由编译器自动实现延迟初始化

//KProperty[]属性数组,Kotlin独有,用于获取Kotlin反射得到的属性,定义反射所需各种参数
static final KProperty[] $$delegatedProperties = new KProperty[]{
    (KProperty)Reflection.property0(new PropertyReference0Impl(
    Reflection.getOrCreateKotlinPackage(LazyInitKt.class, "app_debug"), "lazyInt", "getLazyInt()I"))};

//编译生成属性委托
@NotNull
private static final Lazy lazyInt$delegate;

static {
    //lazyInt$2实现了Function0接口 ,name$2.INSTANCE是该实现类的实例对象
   lazyInt$delegate = LazyKt.lazy((Function0)lazyInt$2.INSTANCE);
}

public static final int getLazyInt() {
   Lazy var0 = lazyInt$delegate;
   return ((Number)var0.getValue()).intValue();
}
private class SynchronizedLazyImpl<out T> {
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    override val value: T
        get() {
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    val typedValue = initializer!!()
                    _value = typedValue
                    initializer = null
                    typedValue
                }
            }
}
  1. 生成一个该属性的附加代理:xxx$delegate;

  2. 在构造器中,将使用lazy(()->T)创建的Lazy实例对象赋值给xxx$delegate;

  3. 当该属性被调用,即其getter方法被调用时返回属性值:

    delegate.getVaule()方法的返回结果是对象xxx$delegate内部的_value属性值,在getVaule()第一次被调用时会将_value进行初始化,往后都是直接将_value的值返回,从而实现属性值的唯一一次初始化。

lateinit无法像by lazy那样自动延迟初始化,且只能用来修饰类属性和对象,不能用于修饰局部量和基本类型(类加载后准备阶段被初始化为默认值),但by lazy从实现来看有一点点代价

有时候,业务逻辑需要围绕某种类型创建包装器。然而,由于额外的堆内存分配问题,它会引入运行时的性能开销。此外,如果被包装的类型是原生类型,性能的损失是很糟糕的,因为原生类型通常在运行时就进行了大量优化,然而他们的包装器却没有得到任何特殊的处理,为此Kotlin在v1.3引入Inline Class——内联类

内联类要求有且仅有一个构造函数的参数,内联类编译期消失,唯一的构造器参数作为内联类实例化时的返回值,内连类的新增方法会变为静态函数,内联类属性的get方法也会变为静态方法被调用,相当于去除包装,直接嵌入实际值或静态调用实现,解决了包装器类在运行时的性能损耗

Kotlin v1.3引的入实验特性(More Details),相对于Java的Signed Numbers在图形相关的编码上更有用

val width = 10u
val height = 20u
val area = width * height

自带类型检查,不会得到无用的负值

/****Kotlin****/
val b = 6u
println(b)
/****Decompile Java****/
//做装箱包装成Java中的UInt,UInt重写了toString方法才能打印
UInt var = UInt.box-impl(b)
System.out.println(var)

用高阶函数实现过滤列表中奇数:

val list = listOf(1, 2, 3, 4, 5)

fun main() {
    filterList {
        it % 2 == 1
    }
}

//非内联高阶函数,参数用Lambda表达式声明一个函数
fun filterList(predicate: (Int) -> Boolean) {
    list.filter(predicate)
}

将相应字节码反编译后的Java实现:

@NotNull
private static final List list = CollectionsKt.listOf(new Integer[]{1, 2, 3, 4, 5});

@NotNull
public static final List getList() {
   return list;
}

public static final void main() {
   filterList((Function1)null.INSTANCE);
}

// $FF: synthetic method
public static void main(String[] var0) {
   main();
}

//Function1类型的predicate被实例化
public static final void filterList(@NotNull Function1 predicate) {
   Intrinsics.checkParameterIsNotNull(predicate, "predicate");
   Iterable $this$filter$iv = (Iterable)list;
   Collection destination$iv$iv = (Collection)(new ArrayList());
   Iterator var6 = $this$filter$iv.iterator();

   while(var6.hasNext()) {
      Object element$iv$iv = var6.next();
      if ((Boolean)predicate.invoke(element$iv$iv)) {
         destination$iv$iv.add(element$iv$iv);
      }
   }
}

改为使用内联函数实现过滤列表中奇数:

val list = listOf(1, 2, 3, 4, 5)

fun main() {
    filterList {
        it % 2 == 1
    }
}

//inline声明filterList为内联函数
inline fun filterList(predicate: (Int) -> Boolean) {
    list.filter(predicate)
}

再次查看相应反编译结果:

@NotNull
private static final List list = CollectionsKt.listOf(new Integer[]{1, 2, 3, 4, 5});

@NotNull
public static final List getList() {
   return list;
}

public static final void main() {
   Iterable $this$filter$iv$iv = (Iterable)getList();
   Collection destination$iv$iv$iv = (Collection)(new ArrayList());
   Iterator var6 = $this$filter$iv$iv.iterator();

   while(var6.hasNext()) {
      Object element$iv$iv$iv = var6.next();
      int it = ((Number)element$iv$iv$iv).intValue();
      //直接将过滤方法和条件嵌入main函数,不会产生Function1类型的predicate对象
      if (it % 2 == 1) {
         destination$iv$iv$iv.add(element$iv$iv$iv);
      }
   }
}

// $FF: synthetic method
public static void main(String[] var0) {
   main();
}

最后的例子使用Iterable的实现对于仅仅是某个范围的遍历显得比较繁重,但还有更糟糕的情形

step 2相当于index+=2,但编译后的实际Java实现并非如此,采取了非常没必要的实现方式;即便是step 1,对应的Java实现依旧如此,说明编译器对此没有任何优化措施,存在性能上的浪费

一个看起来不符合认知(基于Java的认知)的case:

根据反编译字节码的结果来解读(毒):

看似成员方法,实则根据传入的声明类型来定返回值的静态方法,注意与Java的区别!

实现为现有类自由添加自定义函数

//给已有的String类扩展firstChar()函数
fun String.firstChar(): String {
    if (this.isEmpty()) {
        return ""
    }
    return this[0].toString()
}

fun main() {
    val str = "123"
    println(str.firstChar())
}

以静态导入的方式实现,并非真正的增加了成员函数

//receiver即上部代码中的this,在扩展函数中this代表调用函数时点号左侧传递的接收者参数
public static final String firstChar(@NotNull String $receiver) {
   Intrinsics.checkParameterIsNotNull($receiver, "receiver$0");
   CharSequence var1 = (CharSequence)$receiver;
   return var1.length() == 0 ? "" :               String.valueOf($receiver.charAt(0));
}

作为替代xxxUtils.java的更优雅的实现

📚 总结

🔨 扩展

Coroutines on Android:在Android开发中使用协程

Coroutines on Android (part I): Getting the background

Coroutines on Android (part II): Getting started

Coroutines On Android (part III): Real work

上一篇 下一篇

猜你喜欢

热点阅读