我们通过 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
变量和方法实现有疑问的同学,希望能有所帮助。