交互的意义
- 基本上现在的项目没有纯kotlin的代码,所以与Java交互不可避免。
- 就算是纯kotlin代码,Android源码也是Java的,所以交互显得尤为重要。
- 理论上kotlin与Java是100%兼容的,这里记录那些交互方式。
static变量、方法
Kotlin中没有static关键字,不能直接使用静态方法,但是有一些代替品,如object Class,companion object等
object Class
object KtObject {val i = 2val str = "aaa"fun show(){}@JvmStaticfun show2(){}}
�反编译之后
public final class KtObject {private static final int i;@NotNullprivate static final String str;@NotNullpublic static final KtObject INSTANCE;public final int getI() {return i;}@NotNullpublic final String getStr() {return str;}public final void show() {}@JvmStaticpublic static final void show2() {}private KtObject() {}static {KtObject var0 = new KtObject();INSTANCE = var0;i = 2;str = "aaa";}}
object Class结论:
- object 类会生成一个 static 的成员变量
- 成员变量会默认为 static 关键字,并且再 static{} 中初始化
- fun() 并不会生成static关键字,而是生成了一个单例直接调用
- 加上
@JvmStatic注解的方法才会真正生成static方法
companion object
class KtCompanion {companion object {val i = 2val str = "aaa"fun show() = println("show")@JvmStaticfun show2()= println("show2")}fun show3(a: Int) {}/*Only members in named objects and companion objects can be annotated with '@JvmStatic'@JvmStaticfun show4(){}*/}
反编译之后
public final class KtCompanion {private static final int i = 2;@NotNullprivate static final String str = "aaa";@NotNullpublic static final KtCompanion.Companion Companion = new KtCompanion.Companion((DefaultConstructorMarker)null);public final void show3(int a) {}@JvmStaticpublic static final void show2() {Companion.show2(); // 外部调用内部fun}public static final class Companion {public final int getI() {return KtCompanion.i;}@NotNullpublic final String getStr() {return KtCompanion.str;}public final void show() {String var1 = "show";boolean var2 = false;System.out.println(var1);}@JvmStaticpublic final void show2() {String var1 = "show2";boolean var2 = false;System.out.println(var1);}private Companion() {}// $FF: synthetic methodpublic Companion(DefaultConstructorMarker $constructor_marker) {this();}}}
companion object结论:
- 生成一个 static 修饰的
Companion内部类 - 生成一个 static 的
Companion的成员变量 Companion内部的成员变量,会在外部类中生成static的成员变量Companion内部的fun(),不会在外部生成方法Companion内部用@JvmStatic修饰的fun()会在外部生成static方法,即同时存在2个方法,再由外部方法调用内部方法@JvmStatic注解只能用在object Class或者companion object内部
Kotlin调用static
fun callKtObject() {println("KtObject: ${KtObject.i}, ${KtObject.str}")KtObject.show()KtObject.show2()}fun callKtCompanion() {println("KtCompanion: ${KtCompanion.i}, ${KtCompanion.str}")KtCompanion.show()KtCompanion.show2()/*无法调用,必须创建对象再调用KtCompanion.show3()*/}fun callJavaStatic(){println("KtCompanion: ${JavaStatic.i}, ${JavaStatic.str}")JavaStatic.show()}
反编译之后
public static final void callKtObject() {String var0 = "KtObject: " + KtObject.INSTANCE.getI() + ", " + KtObject.INSTANCE.getStr();boolean var1 = false;System.out.println(var0);KtObject.INSTANCE.show();KtObject.show2();}public static final void callKtCompanion() {String var0 = "KtCompanion: " + KtCompanion.Companion.getI() + ", " + KtCompanion.Companion.getStr();boolean var1 = false;System.out.println(var0);KtCompanion.Companion.show();KtCompanion.Companion.show2(); // 加上@JvmStatic也调用了内部方法}public static final void callJavaStatic() {String var0 = "KtCompanion: 1, javaString";boolean var1 = false;System.out.println(var0);JavaStatic.show();}
结论:
- kotlin调用kotlin、java的static方法都可以直接调用
- kotlin调用
object Class的方法实际上加了.INSTANCE,而调用@JvmStatic方法是直接调用的 - kotlin调用
companion object的所有方法都加了.Companion修饰
Java调用static
public class CallTestJava {public static void main(String[] args) {CallTestJava testJava = new CallTestJava();testJava.callKtObject();testJava.callKtCompanion();testJava.callJavaStatic();}private void callKtObject() {System.out.println("KtObject: " + KtObject.INSTANCE.getI() + KtObject.INSTANCE.getStr());KtObject.INSTANCE.show();KtObject.show2();}private void callKtCompanion() {System.out.println("KtCompanion: " + KtCompanion.Companion.getI() + KtCompanion.Companion.getStr());KtCompanion.Companion.show();KtCompanion.show2(); // @JvmStatic 确实生成了2个方法KtCompanion.Companion.show();}// java调用java不再研究private void callJavaStatic(){}}
结论:
- java调用
object Class的方法都需要加上.INSTANCE - java调用
object Class的@JvmStatic方法是直接调用,毕竟生成的就是java static方法 - java调用
companion object的方法都需要加.Companion - java调用
companion object的@JvmStatic方法可是直接调用,也可以加.Companion,因为生成的方法就有2个
Kotlin顶部成员、函数
kotlin的成员、函数可以写在class外部,java则不行
kotlin顶部函数定义
var outStr = "外部变量"val outI by lazy {2}fun outShow() {println("我是kotlin外部方法,没在class内")}inline fun outShow2(info: String, show: (str: String) -> Unit) {show.invoke(info)}
反编译
public final class KtOutKt {@NotNullprivate static String outStr = "外部变量";@NotNullprivate static final Lazy outI$delegate;@NotNullpublic static final String getOutStr() {return outStr;}public static final void setOutStr(@NotNull String var0) {Intrinsics.checkNotNullParameter(var0, "<set-?>");outStr = var0;}public static final int getOutI() {Lazy var0 = outI$delegate;Object var1 = null;Object var2 = null;boolean var3 = false;return ((Number)var0.getValue()).intValue();}public static final void outShow() {String var0 = "我是kotlin外部方法,没在class内";boolean var1 = false;System.out.println(var0);}public static final void outShow2(@NotNull String info, @NotNull Function1 show) {int $i$f$outShow2 = 0;Intrinsics.checkNotNullParameter(info, "info");Intrinsics.checkNotNullParameter(show, "show");show.invoke(info);}static {outI$delegate = LazyKt.lazy((Function0)null.INSTANCE);}}
结论:
- kotlin顶部函数会根据文件名生成一个
文件名+Kt的的Java Class - kotlin顶部成员变量生成static的成员变量
- kotlin顶部函数生成static的方法
- kotlin的高级属性都生效(委托属性、高级函数、lambda表达式等)
tips:顶部函数、成员在同包名下是全局性的,同包名下即使不同的文件名,也不同有相同命名
kotlin调动顶部函数等
fun main() {outIoutStroutShow()outShow2("可莉来帮忙") { a: String ->println("哈哈!$a")}}
反编译后
public final class CallTestKtKt {public static final void main() {KtOutKt.getOutI();KtOutKt.getOutStr();KtOutKt.outShow();String info$iv = "可莉来帮忙";int $i$f$outShow2 = false;int var3 = false;String var4 = "哈哈!" + info$iv;boolean var5 = false;System.out.println(var4);}// $FF: synthetic methodpublic static void main(String[] var0) { // 与泛型的桥接方法类似,为了适配多态main();}}
结论:
- 常规调用即可
- 顶部内联函数调用后生效,直接写在代码中
Java调动顶部函数等
public class CallTestJava {public static void main(String[] args) {int outI = KtOutKt.getOutI();String outStr = KtOutKt.getOutStr();KtOutKt.outShow();KtOutKt.outShow2("java参数", s -> {System.out.println(s);return Unit.INSTANCE;});}}
结论:
- 调用kotlin对应的
文件名+Kt - kotlin的内联函数,转换为Funtion接口,使用的还是回调的方式,内联函数不生效
关键字冲突
Kotlin的Java的关键字有不相同之处,如果互相定了对面的关键字变量、包名等,应该如何处理
包名、类名带关键字
- Java中包名、类名不允许使用关键字,直接报错,关键字包名下不允许创建Java Class文件
- Kotlin中可以使用包名、类名为关键字,但是引入时会有一些变化,关键字
``kotlin package com.xxd.kt.interaction.keyword.in`
class AAA { }
class in {
}
<a name="vc3jB"></a>## 成员变量带关键字```kotlinclass KtKeyword {companion object {// java关键字val static = 1// 调用java文件中的kotlin关键字val i = JavaKeyword.`in`}}
public class JavaKeyword {// in 是kotlin关键字public static final int in = 1;public static void main(String[] args) {// 主动加上set、get方法解决关键字冲突问题KtKeyword.Companion.getStatic();}}
结论:
- Kotlin调用Java中定义的Kotlin关键字变量通过````符号来解决
- Java调用Kotlin中定义的Java关键字变量通过
get、set方法解决
Kotlin调用Java带来的null问题
Kotlin中所有变量、参数、返回值等都必须名字定义可null or 非null,但是Java中不是这样的,那么Kotlin应该如何调用
public class JavaProvider {@Nullablepublic static String str1 = "111";@NotNullpublic static String str2 = "222";// 没有标记的Java变量public static String str3 = "333";}
fun main() {println(JavaProvider.str1?.length)println(JavaProvider.str2.length)// 此变量可能产生NullPointerExceptionprintln(JavaProvider.str3.length)}
结论:
- 有标记
@Nullable、@NotNull的变量可以明确知道是否可null - 没有标记的变量会提示!,需要自己推断,可能产生
NullPointerException
解决方案:使用一个本地变量接收,再处理
// 推荐方案val str3: String? = JavaProvider.str3println(str3?.length)
Kotlin、Java的Class不同
Kotlin的Class是不同于Java的,通过::调用
Kotlin中Class的类型
class KtClass {companion object{val clazz = KtClass::class // kotlin类的classval clazz2 = KtClass::class.java // kotlin类对应的java classval clazz3 = JavaClass::class // java类的classval clazz4 = JavaClass::class.java // java类对应的java class}}
反编译
public final class KtClass {@NotNullprivate static final KClass clazz = Reflection.getOrCreateKotlinClass(KtClass.class);@NotNullprivate static final Class clazz2 = KtClass.class;@NotNullprivate static final KClass clazz3 = Reflection.getOrCreateKotlinClass(JavaClass.class);@NotNullprivate static final Class clazz4 = JavaClass.class;@NotNullpublic static final KtClass.Companion Companion = new KtClass.Companion((DefaultConstructorMarker)null);}
结论:
- 在Kotlin中调用
::class获取到的都是KClass类型 - 在Kotlin中调用
::class.java获取到的都是Class类型,这才是对应的Class - 与文件的后缀名是.java or .kt 无关,只要是kotlin中调用,都是一样的,接收参数也一样
回调的接收方式
Java中需要回调的地方可以写一个接口,Kotlin中回调可以写接口、高级函数等,那么是否可以互相使用lambda表示式呢
public class JavaTest {// Java中都可以使用lambda表达式private KtCallBack1 cb1 = () -> { };private KtCallBack2 cb2 = () -> { };private JavaCallBack cb3 = () -> { };public interface JavaCallBack {void call();}}
fun main() {// 调用Java的接口,可以使用lambdaval cb = JavaTest.JavaCallBack { }// 调用Kotlin的接口,不可以使用lambdaval cb2 = object : KtCallBack1 {override fun call() {}}// 调用Kotlin的 fun接口,可以使用lambdaval cb3 = KtCallBack2 { }}interface KtCallBack1 {fun call()}fun interface KtCallBack2 {fun call()}
结论:
- Java中调用Java、kotlin接口都可使用lambda表示式
- Kotlin调用Java接口可以使用lambda表达式
- Kotlin调用Kotlin接口不可以使用lambda表达式,但是加上
fun的接口可以用
