非局部返回

https://www.kotlincn.net/docs/reference/returns.html
https://www.kotlincn.net/docs/reference/inline-functions.html
上面的两篇doc都提到了非局部返回的问题。

问题

Kotlin中,支持内联函数和lambda,这引入了非局部返回的问题。

  1. fun foo() {
  2. listOf(1, 2, 3, 4, 5).forEach {
  3. if (it == 3) return // 非局部直接返回到 foo() 的调用者
  4. print(it)
  5. }
  6. println("this point is unreachable")
  7. }

上面的代码中,forEach调用时传入的lambda表达式内有一句return,如果把lambda看作是类似Java的匿名内部类语法糖,那么return自然应该导致本次lambda调用返回,但是,在kotlin中,上面的return语句的效果是直接导致foo()函数返回。
为什么会这样?这种行为其实是内联函数的副作用。因为forEach是一个内联函数,因此我们看到的.forEach调用并不会导致栈上出现新的函数调用记录,forEach被直接展开为一段代码。如果这段代码中出现了return,那么,最近的函数调用记录会被pop出栈,也就是函数foo,会返回。

限制

上面的问题对于有其他语言背景的人而言是非常意外的,不了解这种行为的人很容易写出错误的代码。
此外,虽然对于非内联函数而言,这种问题应该是不存在的,但问题是,在写代码或者读代码时,我们并不知道哪些函数是内联的,如果kotlin规定内联函数的lambda内的return效果是导致外层的函数返回,而非内联的lambda内的return又导致的是本lambda调用返回,就会对程序员们造成沉重的负担,迫使我们每看到一个lambda表达式中出现了return语句都要想着这个问题,去查一下手册来确定具体行为。
因此,kotlin对lambda表达式施加了这样一条限制:仅内联函数中可以使用无限定的return语句。也就是说,只要我们在lambda中看到无任何限定的return语句,就可以确定该语句的意图是导致外层函数返回。
另外,不论lambda表达式有没有被内联展开,如果想要从本次lambda调用返回,就必须使用标签来限定return语句,明确指定要返回到那里,下面的例子是显式标签,不过事实上kotlin也支持隐式标签,写起来也没有那么麻烦,具体例子还是参考这里

  1. fun foo() {
  2. listOf(1, 2, 3, 4, 5).forEach lit@{
  3. if (it == 3) return@lit // 局部返回到该 lambda 表达式的调用者,即 forEach 循环
  4. print(it)
  5. }
  6. print(" done with explicit label")
  7. }

和Java互操作

https://www.kotlincn.net/docs/reference/java-interop.html

在kotlin中调用java

  • kotlin中使用对象属性时,相当于调用Java对象的getter、setter方法:
    • Java对象定义了getter的属性,对Kotlin来说是只读属性;
    • Java对象定义了getter和setter的属性,对Kotlin来说是读写属性;
    • Java对象若定义了setter的属性,对Kotlin来说作为属性不可见,因为Kotlin中不存在write-only属性;
  • 返回Void的方法,被Kotlin当作是返回Unit的,该返回的Unit对象可以被Kotlin当成普通Unit对象访问;
  • kotlin中的某些关键字在java中不是关键字,因此可以被java当作普通名称使用,在kotlin中使用这种名称时,需要用 来将名称修饰起来;
  • Java类型是没有是否可空的概念的,因此在使用Java类型时,即可以将这个类型当成可空的,也可当成不可空的,这种特殊的Java类型叫做“平台类型”,但当我们把java类型当成不可空的类型使用时,kotlin会对对象进行非空断言,该行为可能导致运行时的异常;
    • 无法在Kotlin源码中明确声明一个类型为“平台类型”,但编译器和IDE需要将这种类型和Kotlin的可空类型(T?)和不可空类型(T)区分开来,它们使用 T! 代表平台类型;
    • 一些java库提供了Nullable注解,有Nullable注解的Java对象可以被Kotlin明确为可空/不可空类型,kotlin支持几乎所有主流的java库的Nullable注解;
  • 类型映射:Kotlin中没有提供原生基础类型,java中的int在kotlin中对应的类型为kotlin.Int,因此kotlin会在编译时将java的int转换为kotlin.Int,不过如果kotlin发现我们的代码中并没有使用kotlin.Int定义的方法,就不会进行转换,毕竟int还是比kotlin.Int更轻量;
    • 所有原生类型都有映射,当映射不是必要的时,kotlin就不会映射,对于kotlin开发者而言可以假定映射总是发生,kotlin内部会对某些不需要映射的场景优化,但不影响语言上提供的概念模型;
    • 一些非原生的类型也会被映射成kotlin内置类型:
      • java.lang.Object -> kotlin.Any!
      • java.lang.Cloneable -> kotlin.Cloneable!
      • java.lang.String -> kotlin.String!
    • 此外,java的基础类型的包装类型会被映射成kotlin的对应可空类型;
    • java的集合类型本身并没有表明自己是否可变,而kotlin集合类型分为Mutable的可变类型(MutableList)和不可变类型(List),Java的集合类型被映射为一种特殊类型:(Mutable)List,意思是可以被当作可变或者不可变的,但如果Java集合本身是不可变的,却被赋值到可变kotlin类型,那么被修改就会像java那样触发检查逻辑,并抛出异常;

  • 在java中调用kotlin