經過字節碼看原理,帶你去找kotlin中的static方法

kotlin在被欽定爲Android的官方開發語言後,愈來愈多的Android開發者投向kotlin的懷抱。儘管kotlin兼容Java,但在使用上仍是有很大不一樣的,就像static關鍵字,咱們能夠用companion object來替代static,當咱們用反射去調用時,會發現調用時並不像static那樣直接,筆者在平常使用中就遇到這樣的問題,想拿反射去調用靜態方法時沒法調用,因此便經過字節碼的實現來一窺究竟,順便水一篇文章(●>∀<●)。java

1、如何查看kotlin字節碼

咱們經過Tools->Kotlin->Show Kotlin bytecode打開Kotlin字節碼界面,查看Kotlin文件的字節碼形式。界面以下:bash

2、Object單例看static

Kotlin中,咱們能夠經過Object來直接實現一個單例,經過對Object單例中方法的調用來實現相似於Javastatic方法的調用。app

object MyObject {
    val x = "x"

    public fun foo(): String {
        return x
    }
}
複製代碼

對於這個簡單的Object單例,咱們看到的字節碼是這樣的(省略部分字節碼):ui

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變量來實現單例效果。spa

因此咱們在Java語言中調用foo()方法是這樣的,即拿到INSTANCE靜態變量再繼續調用。code

3、Companion Object單例看static

這一次,咱們經過Companion Object伴生對象來實現靜態的變量和方法調用,代碼以下:cdn

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單例的調用是這樣的blog

4、經過@JvmStatic實現Java中的靜態方法

經過以上兩個例子,咱們發現,在咱們聲明的單例中,變量是採用了static修飾的,咱們經過反射能夠直接拿到變量。而方法都沒有使用static修飾。若是不加處理,在咱們用Java進行反射調用時,咱們沒法對foo()方法像Javastatic方法進行直接的反射調用,而要經過Object單例中的INSTANCE或者使用Companion Object單例時的Companion靜態變量,間接地進行反射調用。開發

那麼,咱們可不能夠像對這些單例的方法,進行Javastatic方法的反射調用呢?這時候咱們就要使用@JvmStatic註解。

這時候咱們就能夠看到foo()方法也被static修飾了,這樣咱們在調用foo()方法的方式和在Java調用時的是一致的了。

5、結論

從上面咱們能夠看到,若是不經過@JvmStatic註解,kotlin在字節碼中是不產生static方法的,固然咱們在kotlin使用中是能夠直接調用,如MyClass.foo()的,而放到Java上表現就明顯不一樣了。這篇文章主要是寫給Java轉向kotlin時對kotlinstatic變量和方法實現有疑問的同窗,但願能有所幫助。

相關文章
相關標籤/搜索