这次就主要来谈谈kotlin和java互操作的问题
kotlin出来的使命就是为了解决java的模板问题和一些冗长的问题所以kotlin天生就很好的支持了java 所以我们在java代码中可以很好的引用java的类库和代码 但是在java中调用kotlin的代码就没那么容易了
首先讲讲编译过程,如果一个项目里有kotlin代码和java代码那么编译时 kotlin编译器会先为java代码存根来支持他们之间的依赖 然后在使用java编译器这样就能运行了 因为kotlin代码和java代码在字节码层面上是没有差异的
首先是静态方法
我们知道在kotlin中是没有静态的方法的 在kotlin来表示类级别的是伴生对象 所以我们可以在伴生对象的方法上加上注解 @JvmStatic 这样的意义在于可以让java以静态代码的格式来调用伴生队象的方法 但是实质一个实例方法
//java文件
public class Test {
public static void main(String[] args) {
Book.Companion.hi();//这里
}
}
//kotlin文件
class Book {
companion object{
fun hi(){
println("I am a book")
}
}
}
//java文件
public class Test {
public static void main(String[] args) {
Book.show.hi();//这里
}
}
//kotlin文件
class Book {
companion object show /*这里哦 指定了名字*/{
fun hi(){
println("I am a book")
}
}
}
//java文件
public class Test {
public static void main(String[] args) {
Book.hi();/*这里*/
}
}
//kotlin文件
class Book {
companion object show{
@JvmStatic/*这里*/
fun hi(){
println("I am a book")
}
}
}
public final class Book {
@NotNull//单例模式
public static final Book.show show = new Book.show((DefaultConstructorMarker)null);
/*-----------------------重点-----------------------------------*/
@JvmStatic /*外部类的桥接方法*/
public static final void hi() {
show.hi();
}
public static final class show {
@JvmStatic/*真正的实现逻辑*/
/*-----------------------重点-----------------------------------*/
public final void hi() {
String var1 = "I am a book";
System.out.println(var1);
}
//构造器私有化
private show() {
}
// $FF: synthetic method
public show(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
可以看见hi()方法并为变成真正的静态方法而是在外部类又包装了一个静态的方法 但是具体的逻辑还是在内部类的hi()方法
Kotlin 重载运算符
相信用过都说好 ,但是java并不支持所以怎么办呢 如果kotlin实现了运算符重载,那java只能调用对应方法了 例如 + --> plus()
//java 代码
public class Test {
public static void main(String[] args) {
new Book().plus(new Book());
}
}
//kotlin代码
class Book {
operator fun plus(book : Book){
println("add a book price")
}
}
kotlin当我们访问属性的时候 实际上是访问了我们的getter / setter 但是java没有这么智能就只能手动调用getter / setter
//java代码
public class Test {
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.getName());
}
}
//kotlin代码
class Book {
val name = "Kotlin VS Java"
}
因为他是val变量所以只有getter方法
传递lambda
java中的lambda必须是个接口 而kotlin的lambda实质上是一个匿名函数
所以传递时 就的导kotlin中的Function
//java代码
public class Test {
public static void main(String[] args) {
Book book = new Book();
book.ok(/*传入的lambda表达式*/() -> {
System.out.println("加油");
return null;
});
}
}
//kotlin代码
class Book {
fun ok (arg : () -> Unit){
arg()
}
}
可以看到不能取消括号 参数写在外面
异常
kotlin中全部都是未检查的异常(运行时异常) 而java中有已检查异常(编译时异常) 和 未检查异常 如果是已检查的异常就必须处理 (抛出 / 捕获)
看下面的代码
//java
public class Test {
public static void main(String[] args) {
Book book = new Book();
try {
book.getContent();
} catch (java.io.FileNotFoundException ex) {
System.out.println("not found");
}
}
}
//kotlin
class Book {
//这里
@Throws(java.io.FileNotFoundException::class)
fun getContent(){
val bytes = java.io.File("aha").readLines()
}
}
在java中java.io.FileNotFoundException 是一个编译时异常 所以如果我们在kotlin中可以不处理但是java用的时候必须处理 所以利用注解 @Throws(java.io.FileNotFoundException::class) 抛出这个异常 这样java才能捕获到 否则就捕获不到 会在编译时抛异常 Exception ‘java.io.FileNotFoundException’ is never thrown in the corresponding try block
带默认参数的函数
java中是没有带默认参数的函数这个特性的 那该怎么实现呢 哈哈 当然是重载
public class Test {
public static void main(String[] args) {
Book book = new Book();
book.getPrice();// 'getPrice(int)' in 'Book' cannot be applied to '()'
}
}
//kotlin 代码
class Book {
fun getPrice(price : Int = 1){
println(price*2)
}
}
//java 代码
public class Test {
public static void main(String[] args) {
Book book = new Book();
book.getPrice();// 'getPrice(int)' in 'Book' cannot be applied to '()'
}
}
//kotlin 代码
class Book {
@JvmOverloads
fun getPrice(price : Int = 1){
println(price*2)
}
}
但是是有弊端的,我们知道java的重载是有以方法签名识别不同的重载的方法的
看下面的代码
class Book {
@JvmOverloads
fun getPrice(price : Int = 1,factor : Int = 2){
println(price*factor)
}
}
//看看反编译的代码
public final class Book {
//1.重载方法
@JvmOverloads
public final void getPrice(int price, int factor) {
int var3 = price * factor;
System.out.println(var3);
}
//2.重载方法
@JvmOverloads
public final void getPrice(int price) {
getPrice$default(this, price, 0, 2, (Object)null);
}
//3.重载方法
@JvmOverloads
public final void getPrice() {
getPrice$default(this, 0, 0, 3, (Object)null);
}
// $FF: synthetic method
public static void getPrice$default(Book var0, int var1, int var2, int var3, Object var4) {
if ((var3 & 1) != 0) {
var1 = 1;
}
if ((var3 & 2) != 0) {
var2 = 2;
}
var0.getPrice(var1, var2);
}
}
这里重载的的一个参数的函数的参数 是 price 但是我们可以在kotlin中为factor利用命名参数的方式调用 但是java就肯定不行了 不能只想更改factor的值而不动price的值 这就是有点不完美性
访问顶级的函数
在字节码的层面 顶级函数会被封装在一个类里 这个类是文件名KT
//kotlin代码
//文件名 : Async01.kt
fun getPop(){
println("hello")
}
//反编译后
public final class Async01Kt {
public static final void getPop() {
String var0 = "hello";
System.out.println(var0);
}
}
@file:JvmName("async02")//这里
fun getPop(){
println("hello")
}
@JvmName(
name = "async02"
)
public final class async02/*这里*/ {
public static final void getPop() {
String var0 = "hello";
System.out.println(var0);
}
}
当然还有在Kotlin中还有很多的注解都是为了解决java和Kotlin的互操作的问题
可以使用 Synchronized 来注解一个方法变成一个synchronized 标记的方法
更多的注解可以看官方的文档
今天就是简单的了解一下kotlin和java的互操作