交互的意义
- 基本上现在的项目没有纯kotlin的代码,所以与Java交互不可避免。
- 就算是纯kotlin代码,Android源码也是Java的,所以交互显得尤为重要。
- 理论上kotlin与Java是100%兼容的,这里记录那些交互方式。
static变量、方法
Kotlin中没有static
关键字,不能直接使用静态方法,但是有一些代替品,如object Class
,companion object
等
object Class
object KtObject {
val i = 2
val str = "aaa"
fun show(){}
@JvmStatic
fun show2(){}
}
�反编译之后
public final class KtObject {
private static final int i;
@NotNull
private static final String str;
@NotNull
public static final KtObject INSTANCE;
public final int getI() {
return i;
}
@NotNull
public final String getStr() {
return str;
}
public final void show() {
}
@JvmStatic
public 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 = 2
val str = "aaa"
fun show() = println("show")
@JvmStatic
fun show2()= println("show2")
}
fun show3(a: Int) {}
/*
Only members in named objects and companion objects can be annotated with '@JvmStatic'
@JvmStatic
fun show4(){}
*/
}
反编译之后
public final class KtCompanion {
private static final int i = 2;
@NotNull
private static final String str = "aaa";
@NotNull
public static final KtCompanion.Companion Companion = new KtCompanion.Companion((DefaultConstructorMarker)null);
public final void show3(int a) {
}
@JvmStatic
public static final void show2() {
Companion.show2(); // 外部调用内部fun
}
public static final class Companion {
public final int getI() {
return KtCompanion.i;
}
@NotNull
public final String getStr() {
return KtCompanion.str;
}
public final void show() {
String var1 = "show";
boolean var2 = false;
System.out.println(var1);
}
@JvmStatic
public final void show2() {
String var1 = "show2";
boolean var2 = false;
System.out.println(var1);
}
private Companion() {
}
// $FF: synthetic method
public 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 {
@NotNull
private static String outStr = "外部变量";
@NotNull
private static final Lazy outI$delegate;
@NotNull
public 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() {
outI
outStr
outShow()
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 method
public 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>
## 成员变量带关键字
```kotlin
class 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 {
@Nullable
public static String str1 = "111";
@NotNull
public static String str2 = "222";
// 没有标记的Java变量
public static String str3 = "333";
}
fun main() {
println(JavaProvider.str1?.length)
println(JavaProvider.str2.length)
// 此变量可能产生NullPointerException
println(JavaProvider.str3.length)
}
结论:
- 有标记
@Nullable
、@NotNull
的变量可以明确知道是否可null - 没有标记的变量会提示!,需要自己推断,可能产生
NullPointerException
解决方案:使用一个本地变量接收,再处理
// 推荐方案
val str3: String? = JavaProvider.str3
println(str3?.length)
Kotlin、Java的Class不同
Kotlin的Class是不同于Java的,通过::
调用
Kotlin中Class的类型
class KtClass {
companion object{
val clazz = KtClass::class // kotlin类的class
val clazz2 = KtClass::class.java // kotlin类对应的java class
val clazz3 = JavaClass::class // java类的class
val clazz4 = JavaClass::class.java // java类对应的java class
}
}
反编译
public final class KtClass {
@NotNull
private static final KClass clazz = Reflection.getOrCreateKotlinClass(KtClass.class);
@NotNull
private static final Class clazz2 = KtClass.class;
@NotNull
private static final KClass clazz3 = Reflection.getOrCreateKotlinClass(JavaClass.class);
@NotNull
private static final Class clazz4 = JavaClass.class;
@NotNull
public 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的接口,可以使用lambda
val cb = JavaTest.JavaCallBack { }
// 调用Kotlin的接口,不可以使用lambda
val cb2 = object : KtCallBack1 {
override fun call() {
}
}
// 调用Kotlin的 fun接口,可以使用lambda
val cb3 = KtCallBack2 { }
}
interface KtCallBack1 {
fun call()
}
fun interface KtCallBack2 {
fun call()
}
结论:
- Java中调用Java、kotlin接口都可使用lambda表示式
- Kotlin调用Java接口可以使用lambda表达式
- Kotlin调用Kotlin接口不可以使用lambda表达式,但是加上
fun
的接口可以用