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