转载

通过字节码看原理,带你去找kotlin中的static方法

我们通过 Tools->Kotlin->Show Kotlin bytecode 打开 Kotlin 字节码界面,查看 Kotlin 文件的字节码形式。界面如下:

通过字节码看原理,带你去找kotlin中的static方法

二、Object单例看static

Kotlin 中,我们可以通过 Object 来直接实现一个单例,通过对 Object 单例中方法的调用来实现类似于 Javastatic 方法的调用。

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 在该类中声明了一个 INSTANCEstatic 变量来实现单例效果。

所以我们在 Java 语言中调用 foo() 方法是这样的,即拿 到INSTANCE 静态变量再继续调用。

通过字节码看原理,带你去找kotlin中的static方法

三、Companion Object单例看static

这一次,我们通过 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 单例的调用是这样的

通过字节码看原理,带你去找kotlin中的static方法

四、通过@JvmStatic实现Java中的静态方法

通过以上两个例子,我们发现,在我们声明的单例中,变量是采用了 static 修饰的,我们通过反射可以直接拿到变量。而方法都没有使用 static 修饰。如果不加处理,在我们用 Java 进行反射调用时,我们无法对foo()方法像 Javastatic 方法进行直接的反射调用,而要通过 Object 单例中的 INSTANCE 或者使用 Companion Object 单例时的 Companion 静态变量,间接地进行反射调用。

那么,我们可不可以像对这些单例的方法,进行 Javastatic 方法的反射调用呢?这时候我们就要使用 @JvmStatic 注解。

通过字节码看原理,带你去找kotlin中的static方法

这时候我们就可以看到 foo() 方法也被 static 修饰了,这样我们在调用 foo() 方法的方式和在 Java 调用时的是一致的了。

通过字节码看原理,带你去找kotlin中的static方法

五、结论

从上面我们可以看到,如果不通过 @JvmStatic 注解,kotlin在字节码中是不产生 static 方法的,当然我们在 kotlin 使用中是可以直接调用,如 MyClass.foo() 的,而放到 Java 上表现就明显不同了。这篇文章主要是写给 Java 转向 kotlin 时对 kotlinstatic 变量和方法实现有疑问的同学,希望能有所帮助。

原文  https://juejin.im/post/5ba5bc996fb9a05cf52ac206
正文到此结束
Loading...