Kotlin object關鍵字詳解

一、object用途
1、用戶標識匿名內部類; 2、對象說明(編譯時生成靜態實例)。

二、修飾內部類對象

   btn.setOnClickListener(object: OnClickListener {
        override fun onClick(p0: View?) {
        }  ......
   })

三、object修飾的類爲靜態類
以object關鍵字修飾的類, 其成員變量都是靜態的, 編譯時當前類的靜態實例INSTANCE。

object TestObject {
    //Kotlin通過類訪問
    var i: Int = 1

    //Java、Kotlin能通過類訪問
    @JvmField
    var iExt: Int = 2

    //Kotlin通過類訪問
    fun testMethod() {
        print("參數值是${i}")
    }

    //Java、Kotlin能通過類訪問
    @JvmStatic
    fun testMethodExt() {
        print("testMethodExt")
    }
}

生成的字節碼如下, 可以看到成員變量i爲私有靜態的(Java不能通過TestObject.i訪問,而是用TestObject.INSTANCE.i訪問), 添加@JvmField註解的參數iExt爲共有靜態的(Java和Kotlin都可以直接訪問參數iExt,即TestObject.iExt);

public final class com.brycegao.basic.TestObject {
  private static int i;  //私有靜態變量

  public static int iExt;  //共有靜態變量

  public static final com.brycegao.basic.TestObject INSTANCE; //Kotlin語言內部默認使用INSTANCE訪問類成員參數和函數

  public final int getI();
    Code:
       0: getstatic     #10                 // Field i:I
       3: ireturn

  public final void setI(int);
    Code:
       0: iload_1
       1: putstatic     #10                 // Field i:I
       4: return

  public final void testMethod();  //無static關鍵字, Java不能通過類訪問該方法
    Code:
       0: new           #21                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #24                 // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #26                 // String 參數值是
       9: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: getstatic     #10                 // Field i:I
      15: invokevirtual #33                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      18: invokevirtual #37                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      21: astore_1
      22: getstatic     #43                 // Field java/lang/System.out:Ljava/io/PrintStream;
      25: aload_1
      26: invokevirtual #49                 // Method java/io/PrintStream.print:(Ljava/lang/Object;)V
      29: return

  public static final void testMethodExt();  //添加註解後訪問屬性是公共靜態的final方法
    Code:
       0: ldc           #52                 // String testMethodExt
       2: astore_0
       3: getstatic     #43                 // Field java/lang/System.out:Ljava/io/PrintStream;
       6: aload_0
       7: invokevirtual #49                 // Method java/io/PrintStream.print:(Ljava/lang/Object;)V
      10: return

  private com.brycegao.basic.TestObject();   //構造函數是私有的,不能在外部類實例化TestObject
    Code:
       0: aload_0
       1: invokespecial #53                 // Method java/lang/Object."<init>":()V
       4: return

  static {};     //說明:靜態代碼塊在類被classloader加載時執行一次,實例化TestObject並賦值到INSTANCE
    Code:
       0: new           #2                  // class com/brycegao/basic/TestObject
       3: dup
       4: invokespecial #69                 // Method "<init>":()V
       7: astore_0
       8: aload_0
       9: putstatic     #71                 // Field INSTANCE:Lcom/brycegao/basic/TestObject;
      12: iconst_1
      13: putstatic     #10                 // Field i:I
      16: iconst_2
      17: putstatic     #73                 // Field iExt:I
      20: return
}

執行testMethod和testMethodExt的區別:
調用testMethod函數會在編譯時添加通過INSTANCE對象訪問testMethod的字節碼; 而testMethodExt函數是共有靜態函數, 可以直接通過類名訪問,在編譯時不需要注入字節碼。
在這裏插入圖片描述
Java和Kotlin訪問object修飾的類有細微差別: Java需要顯示的調動TestObject.INSTANCE訪問其成員函數、變量(沒用註解修飾的); 而Kotlin不能顯示的調用INSTANCE。

    public static void main(String[] args) {
        TestObject.testMethodExt();
        TestObject.INSTANCE.testMethod();

三、伴生對象companion object
1、顧名思義,它是跟當前類相伴產生的, 即當前類被classloader加載時被實例化的一個靜態對象。
2、每個類只能有一個伴生對象;
3、伴生對象的成員變量都是靜態私有的;添加@JvmStatic、@JvmField後在編譯時生成public的訪問方法;
4、編譯時會生成共有靜態的當前類$Companion Companion對象; Kotlin語言訪問時會隱式的通過Companion對象訪問其類成員函數、參數;

示例:
在這裏插入圖片描述
Kotlin會在編譯時添加通過Companion訪問函數、參數的字節碼, 不需要顯示的調用; 而Java在訪問私有靜態屬性/函數時必須顯示的調用.Companion. 。 可以看到字節碼裏2種寫法都調用了Companion對象。
在這裏插入圖片描述
使用Java訪問Kotlin伴生對象時, 如果未添加註解@JvmStatic、@JvmField, 則必須要通過Companion對象訪問,因爲伴生對象的屬性默認都是私有的。
在這裏插入圖片描述

小結:
1、伴生對象實際上是一個共有靜態實例, 名稱爲Companion;
2、Kotlin語言訪問伴生對象時會在編譯時添加訪問Companion對象的字節碼;
3、添加註解@JvmStatic、@JvmField的作用是將private變爲public, 即可以通過類名直接訪問, 否則必須通過Companion對象訪問;
4、伴生對象裏的參數聲明都是靜態的, 運行時存儲在Java堆, 不會被GC回收。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章