Kotlin 的编译流程

Kotlin 代码经过编译后,最终会变成 Java 字节码。Kotlin 和 Java 能够兼容的原因也在于此,Java 和 Kotlin 本质上是在用同一种语言进行沟通。

如何研究 Kotlin?

将 Kotlin 转换成字节码后,再将字节码反编译成等价的 Java 代码。

1
2
3
4
5
6
7
8
9
println("Hello world.") /*
编译
↓ */
LDC "Hello world."
INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V /*
反编译
↓ */
String var0 = "Hello world.";
System.out.println(var0);

Android Studio 操作:Tools -> Kotlin -> Show Kotlin Bytecode ,Decompile

Kotlin 里到底有没有“原始类型”?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// kotlin 代码

// 用 val 定义可为空、不可为空的Long,并且赋值
val a: Long = 1L
val b: Long? = 2L

// 用 var 定义可为空、不可为空的Long,并且赋值
var c: Long = 3L
var d: Long? = 4L

// 用 var 定义可为空的Long,先赋值,然后改为null
var e: Long? = 5L
e = null

// 用 val 定义可为空的Long,直接赋值null
val f: Long? = null

// 用 var 定义可为空的Long,先赋值null,然后赋值数字
var g: Long? = null
g = 6L

Java 反编译代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 反编译后的 Java 代码

long a = 1L;
long b = 2L;

long c = 3L;
long d = 4L;

Long e = 5L;
e = (Long)null;

Long f = (Long)null;

Long g = (Long)null;
g = 6L;

最终 a、b、c、d 被 Kotlin 转换成了 Java 的原始类型 long;而 e、f、g 被转换成了 Java 里的包装类型 Long。

Kotlin 对基础类型的转换规则:

  • 只要基础类型的变量可能为空,那么这个变量就会被转换成 Java 的包装类型。
  • 反之,只要基础类型的变量不可能为空,那么这个变量就会被转换成 Java 的原始类型。

接口语法的局限性

  • 箭头①,代表 Kotlin 接口属性,实际上会被当中接口方法来看待。
  • 箭头②,代表 Kotlin 接口默认实现,实际上还是一个普通的方法。
  • 箭头③,代表 Kotlin 接口默认实现的逻辑是被放在 DefaultImpls 当中的,它成了静态内部类当中的一个静态方法 DefaultImpls.walk()。
  • 箭头④,代表 Kotlin 接口的实现类必须要重写接口当中的属性,同时,它仍然还是一个方法。
  • 箭头⑤,即使 Kotlin 里的 Man 类没有实现 walk() 方法,但是从 Java 的角度看,它仍然存在 walk() 方法,并且,walk() 方法将它的执行流程转交给了 DefaultImpls.walk(),并将 this 传入了进去。这样,接口默认方法的逻辑就可以成功执行了。

Kotlin 接口当中的属性,在它被真正实现之前,本质上并不是一个真正的属性。因此,Kotlin 接口当中的属性,它既不能真正存储任何状态,也不能被赋予初始值,因为它本质上还是一个接口方法。

小结

Kotlin 代码,最终都会被 Kotlin 编译器进行一次统一的翻译,把它们变成 Java 能理解的格式。正是因为 Kotlin 编译器在背后做的这些翻译工作,才可以让我们写出的 Kotlin 代码更加简洁、更加安全。

  • 类型推导,我们写 Kotlin 代码的时候省略的变量类型,最终被编译器补充回来了。
  • 原始类型,虽然 Kotlin 没有原始类型,但编译器会根据每一个变量的可空性将它们转换成“原始类型”或者“包装类型”。
  • 字符串模板,编译器最终会将它们转换成 Java 拼接的形式。
  • when 表达式,编译器最终会将它们转换成类似 switch case 的语句。
  • 类默认 public,Kotlin 当中被我们省略掉 public,最终会被编译器补充。
  • 嵌套类默认 static,我们在 Kotlin 当中的嵌套类,默认会被添加 static 关键字,将其变成静态内部类,防止不必要的内存泄漏。
  • 数据类,Kotlin 当中简单的一行代码“data class Person(val name: String, val age: Int)”,编译器帮我们自动生成很多方法:getter()、setter()、equals()、hashCode()、toString()、componentN()、copy()。

关注我