kotlin
在设计之初就考虑到了与java
的互操作性,故这俩语言可以互相调用,进行一个混合的编
项目组织
Kotlin调用Java
类的定义与使用
java定义的类
package com.xlyotut;
public class JavaStudent {
public String name;
public int age;
public JavaStudent() {
}
public JavaStudent(String name, int age) {
this.name = name;
this.age = age;
}
public static void show() {
System.out.println("Hello World");
}
@Override
public String toString() {
return "JavaStudent{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
kotlin使用java定义的类
package com.xlyotut
fun main() {
val java = JavaStudent("流莹", 18)
println(java) // JavaStudent{name='流莹', age=18}
JavaStudent.show() // Hello World
}
如果java定义了一个变量,其名称为kotlin中的关键字,则需要使用该符号`来进行区分,类似于mysql
的关键字区分
如kotlin的关键字in
,java定义了in
名称的变量或函数
public class JavaStudent {
public double in;
}
则kotlin使用方法为:
fun main() {
val java = JavaStudent("流莹", 18)
java.`in` = 23.98
}
kotlin还可以直接继承java类
class KtStudent(name: String, age: Int): JavaStudent(name, age) {
}
Getter和Setter
默认在kotlin定义的类都会默认生成getter和setter的函数
class KtStudent(val name: String, var age: Int) {
}
编译为java后
public final class KtStudent {
@NotNull
private final String name;
private int age;
public KtStudent(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
}
空安全处理
因为java没有原生的空和可空类型,所以kotlin在处理java的类型时需要特殊的处理,称为平台类型,对于这种类型,空检查是放宽的,因此它们的安全保证与java相同,也就是说部分情况下不会进行空检查
比如如下的java定义类
public class JavaStudent {
public String name; // 默认情况下为null
}
如下在kotlin直接进行使用
fun main() {
val java = JavaStudent()
println(java.name.uppercase()) // 不会进行空安全检查,可能会是null
}
在获取name
时idea会提示该类型为String!
,这个!
意思为可以是String
也可以是String?
某些情况,如使用特定注释,则不会视为平台类型,例如@NotNull
,不为空注解
import org.jetbrains.annotations.NotNull;
public class JavaStudent {
@NotNull public String name = "abc";
}
kotlin里就可以断定为非空类型了
fun main() {
val java = JavaStudent()
var name: String = java.name
}
对于java中的数组
public class JavaStudent {
String[] exams;
}
kotlin对应的是Array
fun main() {
val java = JavaStudent()
val ex1: Array<String> = java.exams
val ex2: Array<out String> = java.exams
val ex3: Array<out String?> = java.exams
val ex4: Array<out String?>? = java.exams
}
类型对照表
java与kotlin的类型转换对照表
数组映射
泛型使用
java类型定义了上下界的泛型
import java.util.List;
public class JavaStudent {
List<? extends Number> data; // 上界为Number
List<? super Integer> data2; // 下界为Integer
}
此时会有如下转换
List<? extends Number> data
成为List<out Number!>!
List<? super Integer> data2
成为List<in Integer!>!
fun main() {
val java = JavaStudent()
// java上界对应kotlin协变
val d1: MutableList<out Number> = java.data
// java下届对应kotlin逆变
val d2: MutableList<in Int> = java.data2
}
java中泛型的原始使用
import java.util.List;
public class JavaStudent {
List data;
}
kotlin中使用可空的Any
类型
fun main() {
val java = JavaStudent()
val d1: MutableList<Any?>? = java.data
}
或者是*
星型投影
fun main() {
val java = JavaStudent()
val d1: MutableList<*> = java.data
}
运算符重载
如下面的kotlin类进行运算符重载
fun main() {
val stu = KtStudent(30)
val stu2 = KtStudent(18)
println(stu + stu2) // age = 48
}
class KtStudent(var age: Int) {
operator fun plus(another: KtStudent): KtStudent {
return KtStudent(age + another.age)
}
override fun toString(): String {
return "age = $age"
}
}
main
函数编译为java后
public final class MainKt {
public static final void main() {
KtStudent stu = new KtStudent(30);
KtStudent stu2 = new KtStudent(18);
System.out.println(stu.plus(stu2));
}
// $FF: synthetic method
public static void main(String[] args) {
main();
}
}
可以看到主要就是调用plus
方法,知道这些我们只需要在java中定义函数签名相同的函数
public class JavaStudent {
int age;
public JavaStudent(int age) {
this.age = age;
}
public JavaStudent plus(JavaStudent other) {
return new JavaStudent(age + other.age);
}
@Override
public String toString() {
return "age = " + age;
}
}
在kotlin中也可以实现同样的效果
fun main() {
val stu = JavaStudent(30)
val stu2 = JavaStudent(18)
println(stu + stu2) // age = 48
}
异常检查
在java中如果有方法可能会抛出异常
import java.io.IOException;
public class JavaStudent {
public void make() throws IOException {
// 。。。
}
}
则在kotlin中可以不捕获(如果你确保代码执行不会抛出异常)
fun main() {
val stu = JavaStudent()
stu.make()
}
也可以捕获
import java.io.IOException
fun main() {
val stu = JavaStudent()
try {
stu.make()
} catch (e: IOException) {
// 。。。
}
}
Object类型
java的Object
类型有多个方法在kotlin的Any
中是没有定义的
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeoutMillis) throws InterruptedException;
// 。。。
如果在kotlin想使用java的Object
方法可以使用类型转换
fun main() {
val stu = JavaStudent()
(stu as Object).wait()
}
不过,kotlin不推荐使用notify
、wait
等线程相关的方法,推荐使用JUC
提供的类型
如果需要在kotlin获取某个类的java对象
fun main() {
val stu = JavaStudent()
val clazz: Class<JavaStudent> = stu.javaClass
}
函数式接口
kotlin支持java的SAM
转换,只要是java中满足要求的函数式接口,都可以开箱即用
package java.lang;
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
fun main() {
val run: Runnable = Runnable {
println("Hello world!")
}
}
包括在函数中一样可以使用
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
fun main() {
val executor = ThreadPoolExecutor(3, 4, 5L, TimeUnit.SECONDS, LinkedBlockingQueue())
executor.execute { println("This running in thread ${Thread.currentThread().name}") } // This running in thread pool-1-thread-1
}
Java调用Kotlin
对象属性
在kotlin中定义类
class KtStudent(var age: Int) {
lateinit var name: String
override fun toString(): String {
return "${name}'s age is $age"
}
}
在java中可以直接使用
public class Main {
public static void main(String[] args) {
KtStudent student = new KtStudent(18);
student.name = "青衣";
student.setAge(3000);
System.out.println(student); // 青衣's age is 3000
}
}
属性不得是open
、override
或const
其中一种,也不能是委托属性
由于java中不存在空类型处理,所以在java中可以直接使用
静态属性
直接在.kt
文件里写一个函数
fun test() {
println("test!")
}
这看起来不属于任何类,那java怎么调用呢
在kotlin编译后,会生成MainKt
的类
public final class MainKt {
public static final void test() {
System.out.println("test!");
}
}
所以在java里可以直接通过MainKt
来调用
public class Main {
public static void main(String[] args) {
MainKt.test();
}
}
在kotlin中的文件顶部使用注解可以自定义字节码文件的名称
@file:JvmName("MainClass")
package com.xlyotut
fun test() {
println("test!")
}
JVM注释
使用JVM
注释生成特定的代码
@JvmStatic
@JVMStatic
是一个用于对象声明(object declarations)和伴生对象(companion objects)中的成员的注解。它的主要用途是生成静态方法:在 Kotlin 中,对象声明和伴生对象的成员默认不是静态的。如果你想在 JVM 上使用静态方法,你可以使用 @JVMStatic
注解来生成一个静态方法。这样,你就可以直接通过类名来调用该方法,而不需要创建对象实例。
如下面的kotlin单例工具类
object Utils {
fun printMessage(message: String) {
println(message)
}
}
在java中进行调用需要附带INSTANCE
,不好看
Utils.INSTANCE.printMessage("Hello World!");
使用该注解可以直接生成静态函数
object Utils {
@JvmStatic
fun printMessage(message: String) {
println(message)
}
}
java中直接进行调用
Utils.printMessage("Hello World!");
Utils
的编译后java类如下
public final class Utils {
@NotNull
public static final Utils INSTANCE = new Utils();
private Utils() {
}
@JvmStatic
public static final void printMessage(@NotNull String message) {
Intrinsics.checkNotNullParameter(message, "message");
System.out.println(message);
}
}
@JvmField
@JVMField
是一个用于属性的注解,它的主要用途是生成字段:在 Kotlin 中,属性默认是通过 getter 和 setter 方法来访问的,而不是直接的字段。如果你想在 JVM 上直接访问属性的字段,你可以使用 @JVMField
注解来生成一个实际的字段。这在某些情况下很有用,比如当你需要与其他 Java 代码或者库进行互操作时。
如下kotlin的类:
class DataClass {
val readOnlyProperty: String = "read-only"
var readWriteProperty: String = "read-write"
}
使用java访问,需要调用get和set方法
DataClass data = new DataClass();
String readOnlyProperty = data.getReadOnlyProperty();
data.setReadWriteProperty("new value");
String readWriteProperty = data.getReadWriteProperty();
而加上该注解后
class DataClass {
@JvmField
val readOnlyProperty: String = "read-only"
@JvmField
var readWriteProperty: String = "read-write"
}
java可以直接访问成员属性了
DataClass data = new DataClass();
String readOnlyProperty = data.readOnlyProperty;
data.readWriteProperty = "new value";
String readWriteProperty = data.readWriteProperty;
DataClass
的编译后java类如下
public final class DataClass {
@JvmField
@NotNull
public final String readOnlyProperty = "read-only";
@JvmField
@NotNull
public String readWriteProperty = "read-write";
}