Kotlin原理

package Day03

fun main(){
/*Kotlin 的编译流程*/

//Kotlin 的代码在运行之前,要先经过编译(Compile)
    println("Hello world.")
//经过编译以后,它会变成类似这样的东西
    /*  Java 的字节码,专门给 JVM 执行的
        LDC "Hello world."
        INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V
    */
//Java 和 Kotlin 本质上是在用同一种语言进行沟通
//Kotlin 和 Java 用的什么语言-----它们用的就是 Java 字节码

android kotlin编译器 kotlin编译器原理_java

/*如何研究 Kotlin?*/

//直接研究 Kotlin 编译后的字节码(明显吃力不讨好)
//将 Kotlin 转换成字节码后,再将字节码反编译成等价的 Java 代码
    /*
    println("Hello world.") *//*
               编译
                ↓            *//*
    LDC "Hello world."
    INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V  *//*
              反编译
                ↓            *//*
    String var0 = "Hello world.";
    System.out.println(var0);
    */


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

// 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 代码

//    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、c 来说,它们两个的类型是不可为空的,所以无论如何都不能为 null,
 对于这种情况,Kotlin 编译器会直接将它们优化成原始类型。
 *
·对于变量 b、d 来说,它们两个的类型虽然是可能为空的,但是它的值不为 null,
 并且,编译器对上下文分析后发现,这两个变量也没有在别的地方被修改。
 这种情况,Kotlin 编译器也会将它们优化成原始类型。
 *
·对于变量 e、f、g 来说,不论它们是 val 还是 var,只要它们被赋值过 null,
 那么,Kotlin 就无法对它们进行优化了。这背后的原因也很简单,
 Java 的原始类型不是对象,只有对象才能被赋值为 null。
*/

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


}

/*接口语法的局限性*/

// Kotlin 代码
interface Behavior {
    // 接口内可以有成员属性
    val canWalk : Boolean

    // 接口方法的默认实现
    fun walk() {
        if (canWalk) {
            println(canWalk)
        }
    }
}
// 等价的 Java 代码
/*
public interface Behavior {
    // 接口属性变成了方法
    boolean getCanWalk();

    // 方法默认实现消失了
    void walk();

    // 多了一个静态内部类
    public static final class DefaultImpls {
        public static void walk(Behavior $this) {
            if ($this.getCanWalk()) {
                boolean var1 = $this.getCanWalk();
                System.out.println(var1);
            }
        }
    }
}
*/

class Man : Behavior{
    override val canWalk: Boolean = true
}
// 等价的 Java 代码
/*
public final class Man implements Behavior {
    private final boolean canWalk = true;

    public boolean getCanWalk() {
        // 关键点 ①
        return this.canWalk;
    }

    public void walk() {
        // 关键点 ②
        Behavior.DefaultImpls.walk(this);
    }
}
*/

private fun testInterface() {
    val man = Man()
    man.walk()
}

android kotlin编译器 kotlin编译器原理_android_02

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

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


/*
* Kotlin 代码,最终都会被 Kotlin 编译器进行一次统一的翻译,
* 把它们变成 Java 能理解的格式。Kotlin 的编译器,
* 在这个过程当中就像是一个藏在幕后的翻译官。
*/
//Kotlin 的每一个语法,最终都会被翻译成对应的 Java 字节码。


/*
* 类型推导,我们写 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()。
*/

android kotlin编译器 kotlin编译器原理_java_03


思考题

package Day03

//思考题:
//为 Person 类增加 isAdult 属性,我们要通过自定义 getter 来实现
class Person(val name: String, var age: Int) {
    val isAdult
        get() = age >= 18
}

//而下面这种写法则是错误的:
class Person2(val name: String, var age: Int) {
    val isAdult = age >= 18
}

/*
* 思考题分析:
转换成 java代码就一清二楚,两种方式的isAdult本质不是同一个东西:
1-通过自定义 getter 来实现的方式,isAdult其实是一个方法。外部每一次调用,
  都是拿最新的age进行计算,所以age的值有变动,isAdult()的结果是最新的。
2-val isAdult = age >= 18 这种方式,isAdault是一个final变量,
  只会在对象新建时,在构造方法中,根据age的值赋值一次。所以,之后age的值如果有变动,
  isAdault值是永远不变的。
*/