一分钟学习kotlin — object

obj.png

object使用来声明对象的,可以声明以下三种对象。

  • 生成匿名类(匿名object)
  • 对象声明(命名object)
  • 伴生对象

对象表达式(匿名object对象)

使用object可以有如下几种用法

  • 只生成一个简单对象
fun foo() {
    val adHoc = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(adHoc.x + adHoc.y)
}
复制代码
  • 继承某些类生成匿名对象
class A{
    var t : A0? =  null;
    fun setListener(a:A0){
        t = a
    }
}

open class A0{
    fun doSome(){
        print("some");
    }
}
fun test(){
    A().setListener(object :A0(){})
}
复制代码

一些重要的特性

  1. 能够实现多继承的匿名类。
open class A(x: Int) {
    public open val y: Int = x
}

interface B { /*……*/ }

val ab: A = object : A(1), B {
    override val y = 15
}
复制代码
  1. 对象表达式中的代码可以访问来自包含它的作用域的变量
fun countClicks(window: JComponent) {
    var clickCount = 0
    var enterCount = 0
    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            clickCount++
        }
        override fun mouseEntered(e: MouseEvent) {
            enterCount++
        }
    })
}
复制代码

匿名对象会在使用的时候立即初始化。

对象声明(命名object对象)

在object关键字后面跟一个名字,就声明了一个对象,我把这叫object 命名对象,比如

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ……
}
//调用
DataProviderManager.registerDataProvider(……)
复制代码

object 命名对象可以有超类。但是不可以在局部作用域中生成。

命名对象会延迟访问,会在第一次被访问到时才被初始化(这是官网的话)。

我把kt文件转成java之后时在static{}中初始化的。如果时这样因该是时类加载时初始化

companion 对象

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}
val instance = MyClass.create()
复制代码

onpanion 是在类加载解析的时候就开始初始化。

转换成java之后

简单对象对应的java代码

   public static final void foo() {
      <undefinedtype> adHoc = new Object() {
         private int x;
         private int y;
         public final int getX() {
            return this.x;
         }
         public final void setX(int var1) {
            this.x = var1;
         }
         public final int getY() {
            return this.y;
         }
         public final void setY(int var1) {
            this.y = var1;
         }
      };
      int var1 = ((<undefinedtype>)adHoc).getX() + ((<undefinedtype>)adHoc).getY();
      System.out.print(var1);
   }
复制代码

可以看到 foo()方发是静态的,并且用了object方法来包装了对象。

有继承的匿名类转换后。直接包object 声明的匿名对象转换成了对应的对象,并且也把调用方法转化成了静态的。

//其他的类方法没有发生变化。   
public static final void test() {
      (new A()).setListener((A0)(new A0() {
      }));
   }
复制代码

继承结构的命名object

public final class DefaultListener extends MouseAdapter {
   public static final DefaultListener INSTANCE;

   public void mouseClicked(@NotNull MouseEvent e) {
      Intrinsics.checkParameterIsNotNull(e, "e");
   }

   public void mouseEntered(@NotNull MouseEvent e) {
      Intrinsics.checkParameterIsNotNull(e, "e");
   }

   static {
      DefaultListener var0 = new DefaultListener();
      INSTANCE = var0;
   }
}
复制代码

将类变成final类型的,且在代码块中初始化了这个对象。这个应该时类加载的时候初始化。

public final class MyClass {
   public static final MyClass.Factory Factory = new MyClass.Factory((DefaultConstructorMarker)null);

   public static final class Factory {
      @NotNull
      public final MyClass create() {
         return new MyClass();
      }
      private Factory() {
      }
      // $FF: synthetic method
      public Factory(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

复制代码

可以看到这里使用了一个静态内部类+静态变量实现的单例。

Factor是一个静态的容器类,里面方法返回对象,外层利用静态变量初始化的特性调用内部容器生成对象。

这个设计主要是基于jvm在加载外部类的过程中是不会加载静态内部类。只有内部类的属性和方法被调用时才会被加载。开始初始化静态属性。有点事延迟加载,而且没有线程安全问题。这种做法在开源项目中比常见。

重点

  • 匿名类(匿名object):在使用他们的地方立即执行,普通的初始化。
  • 对象声明(命名object):延迟初始化,第一次使用时才开始初始化。(反编译为java之后发现时在static{}代码块中初始化的,估计时编译器有做特殊处理了,有点矛盾第一次使用才初始化和普通的类没区别了)
  • companion object 类加载之后就开始出化

针对对象声明(命名object对象)初始化问题,如果有朋友知道麻烦告知一下。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享