- 避免 kotlin 关键字
- 平台类型与空值
- 异常检查
- 调用 Java 函数式接口
混合编程包含两个方面:Kotlin 调用 Java 和 Java 调用 Kotlin。
避免 kotlin 关键字
因为 Kotlin 出现的比 Java 要晚许多,所以在 Java 程序员在给 Java 标识符命名时并没有考虑到哪些是 Kotlin 的关键字。但在 Kotlin 中调用这样的 Java 代码时,则需要将这些关键字用 反引号(`)
括起来。例如 Java 标准输出流 System.in
,如果在 Kotlin 中调用则需要表示为 System.`in`
。
// JavaClass.java
public class JavaClass {
public static JavaClass object = new JavaClass();
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
// main.kt
fun main(args: Array<String>?) {
val obj = JavaClass.`object`
println(obj)
}
// 运行结果:
JavaClass
Process finished with exit code 0
平台类型与空值
前面提到过 平台类型,这些类型在 Java 中声明了一个变量或者返回值,它的类型可能为空,也可能非空。Kotlin 在调用它们时会放弃类型检查。
// Person.java
public class Person {
private String name = "小三";
private int age = 20;
private Date birthDate; // 未初始化,为空值 null
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
}
// mian.kt
fun main(args: Array<String>?) {
val person = Person()
val date = person.birthDate // 1️⃣ 编译器自动推导,为平台类型
println("date = $date")
val date1: Date? = person.birthDate // 指定类型Date?,可为空类型
println("date1 = $date1")
val date2: Date = person.birthDate // 2️⃣指定类型Date,不能为空。
println("date2 = $date2")
}
// 运行结果
date = null
date1 = null
Exception in thread "main" java.lang.IllegalStateException: person.birthDate must not be null
at cn.ak.kotmodule.WwwwKt.main(wwww.kt:21)
上面编写了一个 Java 类 Person
,它的 birthDate
字段没有初始化所以为空值。在 Kotlin 中通过属性访问 Java 中的 setter
或 getter
函数,代码第1️⃣行读取 birthDate
属性赋值给变量 date
,此时 date
的类型是由编译器自动推导出来的,所以 IntelliJ IDEA IDE 表示的平台类型是 Date!
,它可以接收空值。
但是如果明确指定返回值类型,可以使用 Date?
或 Date
。由于 Date?
是可空类型,date1
可以接收空值,而 date2
是非空类型,不能接收空值,因此代码第2️⃣行会发生异常。
我们将上面 mian.kt
反编译回 Java 文件会是什么样子?
public static final void main(@Nullable String[] args) {
Person person = new Person();
// 赋值,但未对date空值检查
Date date = person.getBirthDate();
String var3 = "date = " + date;
boolean var4 = false;
System.out.println(var3);
// 赋值,但未对date1空值检查
Date date1 = person.getBirthDate();
String var8 = "date1 = " + date1;
boolean var5 = false;
System.out.println(var8);
// 临时获取birthDate值存储在临时变量var10000中
Date var10000 = person.getBirthDate();
// 如果var10000这个临时变量是空值,抛出异常
Intrinsics.checkExpressionValueIsNotNull(var10000, "person.birthDate");
// 使用var10000值对 date2 初始化
Date date2 = var10000;
String var10 = "date2 = " + date2;
boolean var6 = false;
System.out.println(var10);
}
从反编译后的代码可以看出,date
和 date1
都没有做空值的检查,只有 date2
通过代码 Intrinsics.checkExpressionValueIsNotNull(var10000, "person.birthDate");
检查了空值,并且是空值会抛出异常。
异常检查
Kotlin 和 Java 在异常检查上有很大的不同,Java 有受检查异常,而 Kotlin 中没有受检查异常。那么当 Kotlin 调用 Java 中的一个函数时,这个函数声明抛出异常,那么 Kotlin 会如何处理?
fun main(args: Array<String>?) {
try {
InputStreamReader(System.`in`).use { isr -> // 1️⃣
BufferedReader(isr).use { reader -> // 2️⃣
val command = reader.readLine() // 3️⃣
println(command)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}
代码第1️⃣行~第3️⃣行是通过 Java 标准输入流从键盘读取字符串,相当于 Kotlin 中的 readLine()
函数。这里创建了两个输入流代码,见代码第1️⃣行和第2️⃣行。一个读取数据的函数见代码第3️⃣行,它们都会抛出 IOException
异常。IOException
在 Java 中是受检查异常,必须要进行捕获或抛出处理,而 Kotlin 中不用必须捕获。
调用 Java 函数式接口
Java 函数式接口中 只有一个抽象函数
的 接口
,简称 SAM (Single Abstract Method),在 Kotlin 中调用 Java 函数式接口非常简单,形式是 接口名{...}
。
// Calculable.java
public interface Calculable {
int calculateInt(int a, int b);
}
// main.kt
fun main(args: Array<String>?) {
val n1 = 10
val n2 = 5
// 实现加法计算的Calculable对象
val f1 = Calculable { a, b -> a + b }
// 实现减法计算的Calculable对象
val f2 = Calculable { a, b -> a - b }
// 调用calculateInt函数进行加法计算
println("$n1 + $n2 = ${f1.calculateInt(n1, n2)}")
// 调用calculateInt函数进行减法计算
println("$n1 - $n2 = ${f2.calculateInt(n1, n2)}")
}
// 运行结果
10 + 5 = 15
10 - 5 = 5
Process finished with exit code 0
网友评论