交互的意义

  1. 基本上现在的项目没有纯kotlin的代码,所以与Java交互不可避免。
  2. 就算是纯kotlin代码,Android源码也是Java的,所以交互显得尤为重要。
  3. 理论上kotlin与Java是100%兼容的,这里记录那些交互方式。

static变量、方法

Kotlin中没有static关键字,不能直接使用静态方法,但是有一些代替品,如object Class
companion object

object Class

  1. object KtObject {
  2. val i = 2
  3. val str = "aaa"
  4. fun show(){}
  5. @JvmStatic
  6. fun show2(){}
  7. }

�反编译之后

  1. public final class KtObject {
  2. private static final int i;
  3. @NotNull
  4. private static final String str;
  5. @NotNull
  6. public static final KtObject INSTANCE;
  7. public final int getI() {
  8. return i;
  9. }
  10. @NotNull
  11. public final String getStr() {
  12. return str;
  13. }
  14. public final void show() {
  15. }
  16. @JvmStatic
  17. public static final void show2() {
  18. }
  19. private KtObject() {
  20. }
  21. static {
  22. KtObject var0 = new KtObject();
  23. INSTANCE = var0;
  24. i = 2;
  25. str = "aaa";
  26. }
  27. }

object Class结论:

  1. object 类会生成一个 static 的成员变量
  2. 成员变量会默认为 static 关键字,并且再 static{} 中初始化
  3. fun() 并不会生成static关键字,而是生成了一个单例直接调用
  4. 加上@JvmStatic注解的方法才会真正生成static方法

companion object

  1. class KtCompanion {
  2. companion object {
  3. val i = 2
  4. val str = "aaa"
  5. fun show() = println("show")
  6. @JvmStatic
  7. fun show2()= println("show2")
  8. }
  9. fun show3(a: Int) {}
  10. /*
  11. Only members in named objects and companion objects can be annotated with '@JvmStatic'
  12. @JvmStatic
  13. fun show4(){}
  14. */
  15. }

反编译之后

  1. public final class KtCompanion {
  2. private static final int i = 2;
  3. @NotNull
  4. private static final String str = "aaa";
  5. @NotNull
  6. public static final KtCompanion.Companion Companion = new KtCompanion.Companion((DefaultConstructorMarker)null);
  7. public final void show3(int a) {
  8. }
  9. @JvmStatic
  10. public static final void show2() {
  11. Companion.show2(); // 外部调用内部fun
  12. }
  13. public static final class Companion {
  14. public final int getI() {
  15. return KtCompanion.i;
  16. }
  17. @NotNull
  18. public final String getStr() {
  19. return KtCompanion.str;
  20. }
  21. public final void show() {
  22. String var1 = "show";
  23. boolean var2 = false;
  24. System.out.println(var1);
  25. }
  26. @JvmStatic
  27. public final void show2() {
  28. String var1 = "show2";
  29. boolean var2 = false;
  30. System.out.println(var1);
  31. }
  32. private Companion() {
  33. }
  34. // $FF: synthetic method
  35. public Companion(DefaultConstructorMarker $constructor_marker) {
  36. this();
  37. }
  38. }
  39. }

companion object结论:

  1. 生成一个 static 修饰的Companion内部类
  2. 生成一个 static 的Companion的成员变量
  3. Companion内部的成员变量,会在外部类中生成static的成员变量
  4. Companion内部的fun(),不会在外部生成方法
  5. Companion内部用@JvmStatic修饰的fun()会在外部生成static方法,即同时存在2个方法,再由外部方法调用内部方法
  6. @JvmStatic注解只能用在 object Class 或者 companion object内部

Kotlin调用static

  1. fun callKtObject() {
  2. println("KtObject: ${KtObject.i}, ${KtObject.str}")
  3. KtObject.show()
  4. KtObject.show2()
  5. }
  6. fun callKtCompanion() {
  7. println("KtCompanion: ${KtCompanion.i}, ${KtCompanion.str}")
  8. KtCompanion.show()
  9. KtCompanion.show2()
  10. /*无法调用,必须创建对象再调用
  11. KtCompanion.show3()*/
  12. }
  13. fun callJavaStatic(){
  14. println("KtCompanion: ${JavaStatic.i}, ${JavaStatic.str}")
  15. JavaStatic.show()
  16. }

反编译之后

  1. public static final void callKtObject() {
  2. String var0 = "KtObject: " + KtObject.INSTANCE.getI() + ", " + KtObject.INSTANCE.getStr();
  3. boolean var1 = false;
  4. System.out.println(var0);
  5. KtObject.INSTANCE.show();
  6. KtObject.show2();
  7. }
  8. public static final void callKtCompanion() {
  9. String var0 = "KtCompanion: " + KtCompanion.Companion.getI() + ", " + KtCompanion.Companion.getStr();
  10. boolean var1 = false;
  11. System.out.println(var0);
  12. KtCompanion.Companion.show();
  13. KtCompanion.Companion.show2(); // 加上@JvmStatic也调用了内部方法
  14. }
  15. public static final void callJavaStatic() {
  16. String var0 = "KtCompanion: 1, javaString";
  17. boolean var1 = false;
  18. System.out.println(var0);
  19. JavaStatic.show();
  20. }

结论:

  1. kotlin调用kotlin、java的static方法都可以直接调用
  2. kotlin调用object Class的方法实际上加了.INSTANCE,而调用@JvmStatic方法是直接调用的
  3. kotlin调用companion object的所有方法都加了.Companion修饰

Java调用static

  1. public class CallTestJava {
  2. public static void main(String[] args) {
  3. CallTestJava testJava = new CallTestJava();
  4. testJava.callKtObject();
  5. testJava.callKtCompanion();
  6. testJava.callJavaStatic();
  7. }
  8. private void callKtObject() {
  9. System.out.println("KtObject: " + KtObject.INSTANCE.getI() + KtObject.INSTANCE.getStr());
  10. KtObject.INSTANCE.show();
  11. KtObject.show2();
  12. }
  13. private void callKtCompanion() {
  14. System.out.println("KtCompanion: " + KtCompanion.Companion.getI() + KtCompanion.Companion.getStr());
  15. KtCompanion.Companion.show();
  16. KtCompanion.show2(); // @JvmStatic 确实生成了2个方法
  17. KtCompanion.Companion.show();
  18. }
  19. // java调用java不再研究
  20. private void callJavaStatic(){}
  21. }

结论:

  1. java调用object Class的方法都需要加上.INSTANCE
  2. java调用object Class@JvmStatic方法是直接调用,毕竟生成的就是java static方法
  3. java调用companion object的方法都需要加.Companion
  4. java调用companion object@JvmStatic方法可是直接调用,也可以加.Companion,因为生成的方法就有2个

Kotlin顶部成员、函数

kotlin的成员、函数可以写在class外部,java则不行

kotlin顶部函数定义

  1. var outStr = "外部变量"
  2. val outI by lazy {
  3. 2
  4. }
  5. fun outShow() {
  6. println("我是kotlin外部方法,没在class内")
  7. }
  8. inline fun outShow2(info: String, show: (str: String) -> Unit) {
  9. show.invoke(info)
  10. }

反编译

  1. public final class KtOutKt {
  2. @NotNull
  3. private static String outStr = "外部变量";
  4. @NotNull
  5. private static final Lazy outI$delegate;
  6. @NotNull
  7. public static final String getOutStr() {
  8. return outStr;
  9. }
  10. public static final void setOutStr(@NotNull String var0) {
  11. Intrinsics.checkNotNullParameter(var0, "<set-?>");
  12. outStr = var0;
  13. }
  14. public static final int getOutI() {
  15. Lazy var0 = outI$delegate;
  16. Object var1 = null;
  17. Object var2 = null;
  18. boolean var3 = false;
  19. return ((Number)var0.getValue()).intValue();
  20. }
  21. public static final void outShow() {
  22. String var0 = "我是kotlin外部方法,没在class内";
  23. boolean var1 = false;
  24. System.out.println(var0);
  25. }
  26. public static final void outShow2(@NotNull String info, @NotNull Function1 show) {
  27. int $i$f$outShow2 = 0;
  28. Intrinsics.checkNotNullParameter(info, "info");
  29. Intrinsics.checkNotNullParameter(show, "show");
  30. show.invoke(info);
  31. }
  32. static {
  33. outI$delegate = LazyKt.lazy((Function0)null.INSTANCE);
  34. }
  35. }

结论:

  1. kotlin顶部函数会根据文件名生成一个 文件名+Kt的的Java Class
  2. kotlin顶部成员变量生成static的成员变量
  3. kotlin顶部函数生成static的方法
  4. kotlin的高级属性都生效(委托属性、高级函数、lambda表达式等)

tips:顶部函数、成员在同包名下是全局性的,同包名下即使不同的文件名,也不同有相同命名

kotlin调动顶部函数等

  1. fun main() {
  2. outI
  3. outStr
  4. outShow()
  5. outShow2("可莉来帮忙") { a: String ->
  6. println("哈哈!$a")
  7. }
  8. }

反编译后

  1. public final class CallTestKtKt {
  2. public static final void main() {
  3. KtOutKt.getOutI();
  4. KtOutKt.getOutStr();
  5. KtOutKt.outShow();
  6. String info$iv = "可莉来帮忙";
  7. int $i$f$outShow2 = false;
  8. int var3 = false;
  9. String var4 = "哈哈!" + info$iv;
  10. boolean var5 = false;
  11. System.out.println(var4);
  12. }
  13. // $FF: synthetic method
  14. public static void main(String[] var0) { // 与泛型的桥接方法类似,为了适配多态
  15. main();
  16. }
  17. }

结论:

  1. 常规调用即可
  2. 顶部内联函数调用后生效,直接写在代码中

Java调动顶部函数等

  1. public class CallTestJava {
  2. public static void main(String[] args) {
  3. int outI = KtOutKt.getOutI();
  4. String outStr = KtOutKt.getOutStr();
  5. KtOutKt.outShow();
  6. KtOutKt.outShow2("java参数", s -> {
  7. System.out.println(s);
  8. return Unit.INSTANCE;
  9. }
  10. );
  11. }
  12. }

结论:

  1. 调用kotlin对应的文件名+Kt
  2. kotlin的内联函数,转换为Funtion接口,使用的还是回调的方式,内联函数不生效

关键字冲突

Kotlin的Java的关键字有不相同之处,如果互相定了对面的关键字变量、包名等,应该如何处理

包名、类名带关键字

  1. Java中包名、类名不允许使用关键字,直接报错,关键字包名下不允许创建Java Class文件
  2. Kotlin中可以使用包名、类名为关键字,但是引入时会有一些变化,关键字 ``kotlin package com.xxd.kt.interaction.keyword.in`

class AAA { }

class in {

}

  1. <a name="vc3jB"></a>
  2. ## 成员变量带关键字
  3. ```kotlin
  4. class KtKeyword {
  5. companion object {
  6. // java关键字
  7. val static = 1
  8. // 调用java文件中的kotlin关键字
  9. val i = JavaKeyword.`in`
  10. }
  11. }
  1. public class JavaKeyword {
  2. // in 是kotlin关键字
  3. public static final int in = 1;
  4. public static void main(String[] args) {
  5. // 主动加上set、get方法解决关键字冲突问题
  6. KtKeyword.Companion.getStatic();
  7. }
  8. }

结论:

  1. Kotlin调用Java中定义的Kotlin关键字变量通过````符号来解决
  2. Java调用Kotlin中定义的Java关键字变量通过getset方法解决

Kotlin调用Java带来的null问题

Kotlin中所有变量、参数、返回值等都必须名字定义可null or 非null,但是Java中不是这样的,那么Kotlin应该如何调用

  1. public class JavaProvider {
  2. @Nullable
  3. public static String str1 = "111";
  4. @NotNull
  5. public static String str2 = "222";
  6. // 没有标记的Java变量
  7. public static String str3 = "333";
  8. }
  1. fun main() {
  2. println(JavaProvider.str1?.length)
  3. println(JavaProvider.str2.length)
  4. // 此变量可能产生NullPointerException
  5. println(JavaProvider.str3.length)
  6. }

结论:

  1. 有标记@Nullable@NotNull的变量可以明确知道是否可null
  2. 没有标记的变量会提示!,需要自己推断,可能产生NullPointerException

解决方案:使用一个本地变量接收,再处理

  1. // 推荐方案
  2. val str3: String? = JavaProvider.str3
  3. println(str3?.length)

Kotlin、Java的Class不同

Kotlin的Class是不同于Java的,通过::调用

Kotlin中Class的类型

  1. class KtClass {
  2. companion object{
  3. val clazz = KtClass::class // kotlin类的class
  4. val clazz2 = KtClass::class.java // kotlin类对应的java class
  5. val clazz3 = JavaClass::class // java类的class
  6. val clazz4 = JavaClass::class.java // java类对应的java class
  7. }
  8. }

反编译

  1. public final class KtClass {
  2. @NotNull
  3. private static final KClass clazz = Reflection.getOrCreateKotlinClass(KtClass.class);
  4. @NotNull
  5. private static final Class clazz2 = KtClass.class;
  6. @NotNull
  7. private static final KClass clazz3 = Reflection.getOrCreateKotlinClass(JavaClass.class);
  8. @NotNull
  9. private static final Class clazz4 = JavaClass.class;
  10. @NotNull
  11. public static final KtClass.Companion Companion = new KtClass.Companion((DefaultConstructorMarker)null);
  12. }

结论:

  1. 在Kotlin中调用::class获取到的都是KClass类型
  2. 在Kotlin中调用::class.java获取到的都是Class类型,这才是对应的Class
  3. 与文件的后缀名是.java or .kt 无关,只要是kotlin中调用,都是一样的,接收参数也一样

回调的接收方式

Java中需要回调的地方可以写一个接口,Kotlin中回调可以写接口、高级函数等,那么是否可以互相使用lambda表示式呢

  1. public class JavaTest {
  2. // Java中都可以使用lambda表达式
  3. private KtCallBack1 cb1 = () -> { };
  4. private KtCallBack2 cb2 = () -> { };
  5. private JavaCallBack cb3 = () -> { };
  6. public interface JavaCallBack {
  7. void call();
  8. }
  9. }
  1. fun main() {
  2. // 调用Java的接口,可以使用lambda
  3. val cb = JavaTest.JavaCallBack { }
  4. // 调用Kotlin的接口,不可以使用lambda
  5. val cb2 = object : KtCallBack1 {
  6. override fun call() {
  7. }
  8. }
  9. // 调用Kotlin的 fun接口,可以使用lambda
  10. val cb3 = KtCallBack2 { }
  11. }
  12. interface KtCallBack1 {
  13. fun call()
  14. }
  15. fun interface KtCallBack2 {
  16. fun call()
  17. }

结论:

  1. Java中调用Java、kotlin接口都可使用lambda表示式
  2. Kotlin调用Java接口可以使用lambda表达式
  3. Kotlin调用Kotlin接口不可以使用lambda表达式,但是加上fun的接口可以用