Kotlin object 总结

Posted Eli Shaw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin object 总结相关的知识,希望对你有一定的参考价值。

object 关键字在 kotlin 中有两个用法,一个连用。一种用作对象表达式,另一种用作对象声明,它还可以与 companion 关键字一起使用,被称为伴生对象。

一、总结

㈠ object 用于对象表达式:
    相当于 Java 中的匿名内部类,与匿名内部类不同点如下:
        1.object 的对象表达式可以实现多个接口或实体类。
        2.object 实现的匿名内部类,在此匿名内部类中使用外部方法中的形参或外部方法中的局部变量,无须使用 final 修饰,可直接操作。

㈡ object 用于对象声明
    相当于自动实现了 Java 中的单例模式。

㈢ object 与 companion 关键字连用
    实现一个伴生对象,在伴生对象中定义的属性跟方法,在 Kotlin 的代码中使用,相当于 Java 中的静态方法与静态变量。只是在 Java 中伴生对象又像是一个静态内部类,如果不使用 const 修饰的属性。需要使用伴生对象名引用属性或方法。
    

二、对象表达式

对象表达式,类似于 Java 中的匿名内部类,如下代码

val handler: Handler = object : Handler() 

    override fun handleMessage(msg: Message?) 
        super.handleMessage(msg)
        when (msg?.what) 
            0 -> mTvText?.text = "我是Handler"
        
    

    上边使用 object 实现了一个 Handler 的匿名内部类,并实现了 handlerMessage 方法,我们将此代码转换为 Java,即可看到

private final Handler handler = (Handler)(new Handler() 
   public void handleMessage(@Nullable Message msg) 
      super.handleMessage(msg);
      Integer var2 = msg != null ? msg.what : null;
      if (var2 != null) 
         if (var2 == 0) 
            TextView var10000 = MainActivity.access$getMTvText$p(MainActivity.this);
            if (var10000 != null) 
               var10000.setText((CharSequence)"我是Handler");
            
         
      

   
);

    从上面代码可以看到,对象表达式,与 Java 中的匿名内部类,相当于是一样的(注:相当于一样,并不代表是一样的,Kotlin 有自己的编译器,并不能与 Java 代码完全对等)。

但是它与 Java 的匿名内部类是有区别的,区别在以下几点:
    1.object 的对象表达式可以实现多个接口或实体类。
    2.object 实现的匿名内部类,在此匿名内部类中使用外部方法中的形参或外部方法中的局部变量,无须使用 final 修饰,可直接操作。

三、对象声明

    Kotlin 使用 object 关键字来声明一个对象,使用 object 声明的对象类似于 Java 中的单例类。

object SingleTest 
    val name: String = "我是测试单例类属性 name"
    fun singleTest() 
        println("我是测试单例类方法 singleTest")
    

上面的代码即定义了一个名为 SingleTest 的单例类,此代码转换成 Java 后如下:

public final class SingleTest 
   @NotNull
   private static final String name = "我是测试单例类属性 name";
   public static final SingleTest INSTANCE;

   @NotNull
   public final String getName() 
      return name;
   

   public final void singleTest() 
      String var1 = "我是测试单例类方法 singleTest";
      System.out.println(var1);
   

   static 
      SingleTest var0 = new SingleTest();
      INSTANCE = var0;
      name = "我是测试单例类属性 name";
   

    从上面的代码中可以看出,在 object 中的属性,都会被修饰为 static 类型。
    虽然在编译成的 Java 代码中没有将构造方法私有。但是,在使用时确实不能实例 object 修饰的类。如下代码会报错:

//这样使用会报错,说明使用 object 修饰的对象类,构造方法默认为 private 了,因此使用实例化单例类会报错。
val singleTest:SingleTest = SingleTest() 

    object实现的单例类在使用上与 Java 也有一些分别,在 Kotlin 中可以直接使用 object单例类名.方法|属性。使用代码如下:
KOTLIN

  SingleTest.singleTest() //对象名.方法名
  SingleTest.name //对象名.属性名

JAVA

  SingleTest.INSTANCE.getName();
  SingleTest.INSTANCE.singleTest();

    从上面的代码中可以看出,在 Java 的类中使用还是要引用 INSTANCE 属性,而 Kotlin 中无需引用。

    这里还有一个注意点,当在一个类中使用 object 定义一个对象时,此对象的特性与伴生对象很相似,代码如下

class Objects 
    object Users 
        val name: String = "我是 object 中的属性 name"
        const val age: String = "我是 object 中的属性 age"
        fun method(): String 
            return "我是 object 中的方法 method"
        
    

上面代码转换成 Java 后如下:

public final class Objects 

   public static final class Users 
      @NotNull
      private static final String name = "我是 object 中的属性 name";
      @NotNull
      public static final String age = "我是 object 中的属性 age";
      public static final Objects.Users INSTANCE;

      @NotNull
      public final String getName() 
         return name;
      

      @NotNull
      public final String method() 
         return "我是 object 中的方法 method";
      

      static 
         Objects.Users var0 = new Objects.Users();
         INSTANCE = var0;
         name = "我是 object 中的属性 name";
      
   

    我们看代码,这与伴生对象很相似,与直接定义的 object 类不同的是,此类是静态的内部类。而直接定义的 object 对象是普通 final 类。
    但是他与伴生对象不同的是,object 静态的内部类被定义成了单例模式的,并且使用 const 修饰的属性还是属于当前定义的单例对象,并没有归属于外部类。

    与伴生对象相同的是,使用 const 修饰的属性,还是 public static 类型的,并且没有定义 getter,setter 方法。

四、伴生对象

    类内部的对象声明可以用 companion 关键字标记,这样它就与外部类关联在一起,我们就可以直接通过外部类访问到对象的内部元素。

class Users 
    companion object Factory 
        val name: String = "我是 companion 中的属性 name"
        const val age: String = "我是 companion 中的属性 age"
        fun method(): String 
            return "我是 companion 中的方法 method"
        
    

上面代码转换成 Java 后如下:

public final class Users 
   @NotNull
   private static final String name = "我是 companion 中的属性 name";
   @NotNull
   public static final String age = "我是 companion 中的属性 age";
   public static final Users.Factory Factory = new Users.Factory((DefaultConstructorMarker)null);


   public static final class Factory 
      @NotNull
      public final String getName() 
         return Users.name;
      

      @NotNull
      public final String method() 
         return "我是 companion 中的方法 method";
      

      private Factory() 
      

      // $FF: synthetic method
      public Factory(DefaultConstructorMarker $constructor_marker) 
         this();
      
   

    从上面的代码可以看出,Java 中的伴生对象相当于是一个静态内部类,并且伴生对象的构造方法是私有的,也就是说伴生对象是无法被实例的。
    再看伴生对象中定义的属性,最终归属于外部类。然而使用 const 修饰的属性被定义为 public static 类型。没有使用 const 修饰的属性,被定义为 private static 类型,并在伴生对象中生成了此属性的 getter 方法。
    因此使用 const 修饰的属性,在使用的时候可以直接 外部类名.伴生对象的属性名 使用。使用代码如下

KOTLIN

  Users.name //在 Kotlin 中其实隐藏了伴生对象的类名
  Users.age // age 使用 const 关键字修饰的,属性于 Users 的公共静态属性
  Users.method() // 访问到伴生对象的内部元素

JAVA

  Users.Factory.getName(); //在 Java 中使用伴生对象,需要引用伴生对象名
  String age = Users.age; //在 Java 中使用 const 修饰的伴生对象属性,必须使用一个变量来接收
  Users.Factory.method(); //在 Java 中使用伴生对象,需要引用伴生对象名


我们可以省略掉该对象的对象名,然后使用 Companion 替代需要声明的对象名:

class Users 
    companion object 
        val name: String = "我是 companion 中的属性 name"
        const val age: String = "我是 companion 中的属性 age"
        fun method(): String 
            return "我是 companion 中的方法 method"
        
    

KOTLIN

  Users.name //在 Kotlin 中其实隐藏了伴生对象的类名
  Users.age // age 使用 const 关键字修饰的,属性于 Users 的公共静态属性
  Users.method() // 访问到伴生对象的内部元素

JAVA

  Users.Companion.getName(); //在 Java 中使用伴生对象,需要引用伴生对象名
  String age = Users.age; //在 Java 中使用 const 修饰的伴生对象属性,必须使用一个变量来接收
  Users.Companion.method(); //在 Java 中使用伴生对象,需要引用伴生对象名

    我们看省略了伴生对象名,在 Kotlin 中使用还是老样子,但是在 Java 中使用,还是需要引用伴生对象名,不同的是,此处伴生对象名是默认的 Companion。

    注意:一个类里面只能声明一个内部关联对象,即关键字 companion 只能使用一次。

    之前有看到说在 object 或 companion object 中定义的属性或方法为静态方法或变量,这是错误的。他们在使用上确实跟静态方法和静态变量一样,但是在 Kotlin 中如果要定义静态方法或静态变量,一定要使用 @JvmStatic 注解修饰的变量或属性才为静态方法或静态变量,并且此关键字只能用在 object 或 companion object 中。

五、const 关键字

    const 只能修饰 val
    const 只允许在 top-level 级别和 object 中声明,使用方式如下:

const val THOUSAND = 1000 //用在类的外面
class Const 
    object MyObject  //用在 object 中
        const val constNameObject: String = "constNameObject"
        val nameObject: String = "nameObject"
    
    companion object  //用在伴生对象中
        const val constNameCompanionObject: String = "constNameCompanionObject"
        val nameCompanionObject: String = "nameCompanionObject"
    

上面代码转换成 Java 后如下:

public final class ConstKt 
   public static final int THOUSAND = 1000;

// Const.java
package cn.eli.androidkotlin;

import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
import org.jetbrains.annotations.NotNull;


public final class Const 
   @NotNull
   public static final String constNameCompanionObject = "constNameCompanionObject";
   @NotNull
   private static final String nameCompanionObject = "nameCompanionObject";
   public static final Const.Companion Companion = new Const.Companion((DefaultConstructorMarker)null);

   public static final class MyObject 
      @NotNull
      public static final String constNameObject = "constNameObject";
      @NotNull
      private static final String nameObject = "nameObject";
      public static final Const.MyObject INSTANCE;

      @NotNull
      public final String getNameObject() 
         return nameObject;
      

      static 
         Const.MyObject var0 = new Const.MyObject();
         INSTANCE = var0;
         nameObject = "nameObject";
      
   

   public static final class Companion 
      @NotNull
      public final String getNameCompanionObject() 
         return Const.nameCompanionObject;
      

      private Companion() 
      

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) 
         this();
      
   

使用方式如下:KOTLIN

THOUSAND //在 kotlin 中可以直接使用定义在 top-level 级别的 const 常量

Const.MyObject.constNameObject //在 kotlin 中,定义在 object 中的 const val 与 val 使用上没有分别
Const.MyObject.nameObject //在 kotlin 中,定义在 object 中的 const val 与 val 使用上没有分别

Const.constNameCompanionObject //在 kotlin 中,定义在 companion object 中的 const val 与 val 使用上没有分别
Const.nameCompanionObject //在 kotlin 中,定义在 companion object 中的 const val 与 val 使用上没有分别

JAVA

int thousand = ConstKt.THOUSAND; //在 Java 使用定义在 top-level 级别的 const 常量,需要使用 Kotlin 生成的类名

String constNameObject = Const.MyObject.constNameObject; //在 Java 中,定义在 object 中的 const val 与 val 使用上有区别
String nameObject = Const.MyObject.INSTANCE.getNameObject(); //在 Java 中,定义在 object 中的 const val 与 val 使用上有区别

String constNameCompanionObject = Const.constNameCompanionObject; //在 Java 中,定义在 companion object 中的 const val 与 val 使用上有区别
String nameCompanionObject = Const.Companion.getNameCompanionObject(); //在 Java 中,定义在 companion object 中的 const val 与 val 使用上有区别

从上面的代码总结如下

㈠ const 用在 top-level 级别
    会在当前类的第一行生成一个:"当前类名+Kt" 的类,在此类中定义 public static final 常量。在 Kotlin 中可以直接使用常量的名字,而在 Java 中需要加上 Kotlin 生成的“当前类名+Kt”。

㈡ const 用在 object 
    无论是否使用 const 修饰的属性,他都属于当前定义的对象。在使用时需要引用对象的名字,即:外部类.对象.属性

㈢ const 用在 companion object(伴生对象)
    const 修饰的属性,将被定义在外部类中,在 Java 中可直接使用 外部类名.const 属性即可。

㈣ const val 与 val 的区别
    const val 修饰的属性为 public staitc final 类型,可直接引用。
    val 修饰的属性定义在普通类中为 private final 类型,定义在对象、伴生对象中为 private static final 类型。无论定义在哪里,都会为 val 修饰的属性创建 getter 方法,在访问的时候,实际调用的是其 getter 方法。
 

以上是关于Kotlin object 总结的主要内容,如果未能解决你的问题,请参考以下文章

区别Kotlin中的object和companion object关键字

kotlin-object关键字与单例模式

Kotlin 使用和优势

在Objective-C中浅谈面向对象

Kotlin之框架模式MVP总结和测试代码

kotlin用object实现单例模式,companion object与java静态