一、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回收。