我们通过 Tools->Kotlin->Show Kotlin bytecode 打开 Kotlin 字节码界面,查看 Kotlin 文件的字节码形式。界面如下:
在 Kotlin 中,我们可以通过 Object 来直接实现一个单例,通过对 Object 单例中方法的调用来实现类似于 Java 中 static 方法的调用。
object MyObject {
val x = "x"
public fun foo(): String {
return x
}
}
复制代码
对于这个简单的 Object 单例,我们看到的字节码是这样的(省略部分字节码):
private final static Ljava/lang/String; x = "x" public final getX()Ljava/lang/String; ... public final setX(Ljava/lang/String;)V .. public final foo()Ljava/lang/String; ... public final static Lcom/tanzhouedu/testapplication/MyObject; INSTANCE 复制代码
可以看到, Kotlin 在该类中声明了一个 INSTANCE 的 static 变量来实现单例效果。
所以我们在 Java 语言中调用 foo() 方法是这样的,即拿 到INSTANCE 静态变量再继续调用。
这一次,我们通过 Companion Object 伴生对象来实现静态的变量和方法调用,代码如下:
class MyClass {
companion object {
val x = "x"
fun foo(): String {
return x
}
}
}
复制代码
我们看到的字节码是这样的(省略部分字节码):
// access flags 0x1A
private final static Ljava/lang/String; x = "x"
// access flags 0x19
public final static Lcom/windinwork/myapplication/bytecode/MyClass$Companion; Companion
// access flags 0x31
public final class com/windinwork/myapplication/bytecode/MyClass$Companion {
// access flags 0x11
public final getX()Ljava/lang/String;
@Lorg/jetbrains/annotations/NotNull;() // invisible
L0
LINENUMBER 6 L0
INVOKESTATIC com/windinwork/myapplication/bytecode/MyClass.access$getX$cp ()Ljava/lang/String;
ARETURN
...
// access flags 0x11
public final foo()Ljava/lang/String;
...
复制代码
我们来分析一下这段字节码,可以看到,我们在 Companion Object 中声明的变量 x ,编译之后是作为 MyClass 的静态变量存在,而方法getX()和foo()是作为 MyClass$Companion 的成员方法存在。我们可以看到, MyClass 通过一个静态变量 Companion 持有 MyClass$Companion 的引用,所以我们在访问x变量和调用 foo() 方法时,实质上是通过对 Companion 这一静态变量进行方法调用,于是我们在Java中对 Companion Object 单例的调用是这样的
通过以上两个例子,我们发现,在我们声明的单例中,变量是采用了 static 修饰的,我们通过反射可以直接拿到变量。而方法都没有使用 static 修饰。如果不加处理,在我们用 Java 进行反射调用时,我们无法对foo()方法像 Java 的 static 方法进行直接的反射调用,而要通过 Object 单例中的 INSTANCE 或者使用 Companion Object 单例时的 Companion 静态变量,间接地进行反射调用。
那么,我们可不可以像对这些单例的方法,进行 Java 的 static 方法的反射调用呢?这时候我们就要使用 @JvmStatic 注解。
这时候我们就可以看到 foo() 方法也被 static 修饰了,这样我们在调用 foo() 方法的方式和在 Java 调用时的是一致的了。
从上面我们可以看到,如果不通过 @JvmStatic 注解,kotlin在字节码中是不产生 static 方法的,当然我们在 kotlin 使用中是可以直接调用,如 MyClass.foo() 的,而放到 Java 上表现就明显不同了。这篇文章主要是写给 Java 转向 kotlin 时对 kotlin 中 static 变量和方法实现有疑问的同学,希望能有所帮助。