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."":()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."":()V
       4: return

  static {};     //说明:静态代码块在类被classloader加载时执行一次,实例化TestObject并赋值到INSTANCE
    Code:
       0: new           #2                  // class com/brycegao/basic/TestObject
       3: dup
       4: invokespecial #69                 // Method "":()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 object关键字详解_第1张图片
Kotlin会在编译时添加通过Companion访问函数、参数的字节码, 不需要显示的调用; 而Java在访问私有静态属性/函数时必须显示的调用.Companion. 。 可以看到字节码里2种写法都调用了Companion对象。
在这里插入图片描述
使用Java访问Kotlin伴生对象时, 如果未添加注解@JvmStatic、@JvmField, 则必须要通过Companion对象访问,因为伴生对象的属性默认都是私有的。
Kotlin object关键字详解_第2张图片

小结:
1、伴生对象实际上是一个共有静态实例, 名称为Companion;
2、Kotlin语言访问伴生对象时会在编译时添加访问Companion对象的字节码;
3、添加注解@JvmStatic、@JvmField的作用是将private变为public, 即可以通过类名直接访问, 否则必须通过Companion对象访问;
4、伴生对象里的参数声明都是静态的, 运行时存储在Java堆, 不会被GC回收。

你可能感兴趣的:(Kotlin语言)