内容简介
Kotlin 基础篇差不多快要完结了,接下来我们来讲讲 Kotlin 与 Java 之间的爱恨情仇(相互调用)。
众所周知 Kotlin 是完全兼容 Java 的,它们之间的代码是相互可以调用的,但是由于语法上的不同 Java 调用 Kotlin 代码多少有点小别扭。
Kotlin 为我们提供了一些注解,来解决这些别扭。
file:JvmName
还记得包级函数吗?我们 Java
要调用包级函数要如何调用呢(想想包级函数的原理)?
Kot.kt
文件中定义:
package com.qihoo.kot
fun call(){
println("我是包级函数")
}
Java
调用
public class TestJava {
public static void main(String[] args) {
/**
* Java 中调用需要指定类
* 调用方式:Kotlin的文件名+Kt.调用方法()
*
* 好头疼还需要加个 Kt
*/
KotKt.call();
}
}
上面代码注意到
Java
调用还需要加一个Kt
的后缀,如何解决呢?
其实解决这个问题很简单,只需要通过 @file:JvmName("类名")
告诉编译器生成的类名我自己指定。
// 注意这个要注解在包上
@file:JvmName("Kot")
package com.qihoo.kot
/**
* Kotlin 中定义包级函数 call
*/
fun call(){
println("我是包级函数")
}
public class TestJava {
public static void main(String[] args) {
/**
* Java 这里就可以直接使用 Kot 啦
*/
Kot.call();
}
}
温馨提示:配置混淆的时候要注意哦,生成的类名最好别乱改。
JvmField
Kotlin
定义的变量,如果是 val
类型会生成 getXX
方法, var
类型变量会生成 getXX
和 setXX
方法。
Java
想获取这些变量,只能通过 getXX
方法去获取。
Kotlin
定义:
class Code(
val name: String,
val age: Int
) {
var sex: String = "男"
}
Java
调用:
public class TestJava {
public static void main(String[] args) {
Code code = new Code("啊文", 18);
/**
* 由于我们是 val 变量,所以只有 get 方法
*
* Java 中可以直接通过 get 获取
*/
code.getAge();
code.getName();
code.getSex();
}
}
若 Java
中如果不想通过 getXX
或 setXX
来操控变量,可以在 Kotlin
中使用 @JvmField
注解变量即可。
class Code(
@JvmField
val name: String,
@JvmField
val age: Int
) {
@JvmField
var sex: String = "男"
}
JvmStatic
Kotlin
的静态方法是定义在伴生对象中, Java
想调用还需要通过获取伴生对象来进行调用。
class Code {
/**
* 定义伴生对象
*/
companion object {
fun writeCode(){
println("writeCode")
}
}
}
在 Java
中调用,需要通过伴生对象。
public class TestJava {
public static void main(String[] args) {
/**
* 调用伴生对象的方法
*/
Code.Companion.writeCode();
}
}
Kotlin
中通过 @JvmStatic
注解方法,即可解决此问题。
class Code {
/**
* 定义伴生对象
*/
companion object {
/**
* 通过 JvmStatic 注解即可
*/
@JvmStatic
fun writeCode(){
println("writeCode")
}
}
}
通过 @JvmStatic
注解方法,在 Java
中调用,不再需要通过伴生对象。
public class TestJava {
public static void main(String[] args) {
/**
* 调用伴生对象的方法
*/
Code.writeCode();
}
}
补充:在
Kotlin
中定义的object
类(单例类)的方法也可以通过@JvmStatic
注解,可以让Java
中调用时,不需要引用INSTANCE
对象。
/**
* 定义 Kotlin 的 object 类
*/
object Simple {
/**
* 通过 JvmStatic 修饰
*/
@JvmStatic
fun call(){
print("call")
}
}
// Java 尝试调用
public class TestJava {
public static void main(String[] args) {
// 以前的调用方式,还需要通过 INSTANCE 引用
// Simple.INSTANCE.call();
/**
* 直接调用 call 方法
*/
Simple.call();
}
}
JvmOverloads
在 Kotlin
的世界中,存在一种超级好用的参数默认值的功能,我们一般使用这种方式来减少重载方法。但是 Java
没有这项功能呀?那 Java
调用带有默认参数的 Kotlin
方法会怎样呢?
Kotlin
中定义默认参数方法:
class Code {
/**
* 使用了默认参数
*/
fun eat(food: String = "米饭") {
println("吃$food")
}
}
Java
中尝试去调用:
public class TestJava {
public static void main(String[] args) {
Code code = new Code();
/**
* 可以发现,没有默认参数的功能,必须要填写参数
*/
code.eat("大鸡腿");
}
}
从上面的代码可以注意到
Java
调用Kotlin
默认参数方法是必须要传参的。其实这里大伙就要思考下Kotlin
的默认参数难道不是通过函数重载的形式来实现的吗?其实还真不是,具体大家可以反编译下。
那如何解决呢?我们想让默认参数的方法,在 Java
中也能调用,调用的形式就是函数重载。其实我们只需要通过 @JvmOverloads
注解默认参数的 Kotlin
方法即可,这样在编译期就会生成重载方法。
Kotlin
中尝试使用 @JvmOverloads
修饰默认参数方法:
class Code {
/**
* 使用了默认参数,并使用 @JvmOverloads 注解
*/
@JvmOverloads
fun eat(food: String = "米饭") {
println("吃$food")
}
}
Java
中尝试去调用重载的方法:
public class TestJava {
public static void main(String[] args) {
Code code = new Code();
/**
* 可以调用无参的
*/
code.eat();
/**
* 可以传参
*/
code.eat("大鸡腿");
}
}
补充:我曾经遇到一个坑,在
Andorid
中自定义View
的时候,Android
中一定要重载View
的3
个构造方法,并且相互向下调用。在Kotlin
中我通过默认参数就可以方便很多。但是我将自定义的View
加入xml
布局中,直接崩溃了。报错原因是没有重载2
个参数的View
构造方法,为何会这样呢?其实就是因为Kotlin
的默认参数方法,并不是通过重载去实现的,我们只需要通过@JvmOverloads
修饰构造方法即可。
class HorizontalLoadingView
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
View(context, attrs, defStyleAttr) {
}
Synchronized
Kotlin
如何定义一个同步方法呢? Java
中可以通过 synchronized
修饰方法。 Kotlin
只需要通过 @Synchronized
注解方法即可。
Kotlin
同步代码块和Java
的定义形式一样
Volatile
volatile
可见性,在 Java
定义单例的时候用过,它可以保证线程安全 3
大条件之一的可见性。 Kotlin
中只需要通过 @Volatile
注解即可。
file:JvmMultifileClass
可以将 2
个包下包级别函数放在指定类下,必须要配合 @file:JvmName("Utils")
让其生成的包级类一致。这个用处太少了,我感觉没啥用,反而让代码更加的混乱。
总结
通过以上的几个注解,我相信大家对 Java
与 Kotlin
互相调用有一定把握了吧。当然这只是我遇到的问题,不保证全部,不过这些注解足够应付我们实际开发啦。
推荐阅读
--END--