Kotlin 中的 Object

在 Kotlin 中,object 关键字是一个强大的工具,主要用于创建单例模式(Singleton)和伴生对象(Component Object),以及匿名内部类(Anonymous Inner Classes)。 它解决了 Java 中实现单例和静态成员的一些痛点。

1 单例模式

Object 最常见的用途是创建单例类,无需手动实现线程安全的单例模式(饿汉式)。 特点如下:

  • 全局唯一实例,在第一次访问时自动创建;
  • 线程安全,Kotlin 编译器会处理同步问题;
  • 可继承或实现接口;
  • 没有构造函数;
kotlin 复制代码
object DatabaseManager {
    init {
        println("DatabaseManager initialized") // 只会在第一次访问时执行一次
    }

    fun connect() {
        println("Connecting to database...")
    }

    fun disconnect() {
        println("Disconnecting from database")
    }
}

fun main() {
    DatabaseManager.connect() // 第一次访问,触发初始化
    DatabaseManager.disconnect()
    // 所有地方访问的都是同一个 DatabaseManager 实例
}

编译结果:Kotlin 编译器会生成一个名为 DatabaseManager 的类,并在其中创建一个名为 INSTANCE 的静态字段(public static final DatabaeseManger INSTANCE;)来持有这个唯一的实例。代码中对 DatabaseManager 的访问都会被编译为访问 DatabaseManager.INSTANCE

java 复制代码
public final class DatabaseManagerKt {
   public static final void main() {
      DatabaseManager.INSTANCE.connect();
      DatabaseManager.INSTANCE.disconnect();
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}
// DatabaseManager.java
package com.example.java_test;

import kotlin.Metadata;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 8, 0},
   k = 1,
   d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0002\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004J\u0006\u0010\u0005\u001a\u00020\u0004¨\u0006\u0006"},
   d2 = {"Lcom/example/java_test/DatabaseManager;", "", "()V", "connect", "", "disconnect", "java_test_debug"}
)
public final class DatabaseManager {
   @NotNull
   public static final DatabaseManager INSTANCE;

   public final void connect() {
      String var1 = "Connecting to database...";
      System.out.println(var1);
   }

   public final void disconnect() {
      String var1 = "Disconnecting from database";
      System.out.println(var1);
   }

   private DatabaseManager() {
   }

   static {
      DatabaseManager var0 = new DatabaseManager();
      INSTANCE = var0;
      String var1 = "DatabaseManager initialized";
      System.out.println(var1);
   }
}

2 伴生对象(Companion Objects)------ 替代静态成员

用于在类内部创建类似 Java 的静态成员变量。 其特点如下:

  • 在类内部使用 companion object 声明,可省略名称(默认名称为 Companion);
  • 每个类只能有一个伴生对象;
  • 可继承类和实现接口;
  • 本质上是一个外部类的静态内部类的实例,可访问外部类的 private 成员;
kotlin 复制代码
class MyClass {
    private val instanceProperty = "Instance Property"

    companion object Factory {  // 命名伴生对象为 Factory
        const val PI = 3.14159  // 编译时常量(类似 public static final)

        fun create(): MyClass {
            println("Creating MyClass instance")
            return MyClass()
        }

        fun accessPrivate(myClass: MyClass) {
            println(myClass.instanceProperty) // 可以访问外部类的私有成员!
        }
    }
}

// 使用(通过类名直接访问伴生对象成员)
fun main() {
    val pi = MyClass.PI // 访问常量
    val obj1 = MyClass.create() // 调用工厂方法
    val obj2 = MyClass.create() // 调用工厂方法

    MyClass.accessPrivate(obj1) // 访问外部类私有成员需要传入实例
}

编译结果:本质上是一个外部类的静态内部类

java 复制代码
public final class MyClassKt {
   public static final void main() {
      double pi = 3.14159;
      MyClass obj1 = MyClass.Factory.create();
      MyClass obj2 = MyClass.Factory.create();
      MyClass.Factory.accessPrivate(obj1);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}
// MyClass.java
package com.example.java_test;

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

@Metadata(
   mv = {1, 8, 0},
   k = 1,
   d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\u0018\u0000 \u00052\u00020\u0001:\u0001\u0005B\u0005¢\u0006\u0002\u0010\u0002R\u000e\u0010\u0003\u001a\u00020\u0004X\u0082D¢\u0006\u0002\n\u0000¨\u0006\u0006"},
   d2 = {"Lcom/example/java_test/MyClass;", "", "()V", "instanceProperty", "", "Factory", "java_test_debug"}
)
public final class MyClass {
   private final String instanceProperty = "Instance Property";
   public static final double PI = 3.14159;
   @NotNull
   public static final Factory Factory = new Factory((DefaultConstructorMarker)null);

   @Metadata(
      mv = {1, 8, 0},
      k = 1,
      d1 = {"\u0000 \n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0006\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u000e\u0010\u0005\u001a\u00020\u00062\u0006\u0010\u0007\u001a\u00020\bJ\u0006\u0010\t\u001a\u00020\bR\u000e\u0010\u0003\u001a\u00020\u0004X\u0086T¢\u0006\u0002\n\u0000¨\u0006\n"},
      d2 = {"Lcom/example/java_test/MyClass$Factory;", "", "()V", "PI", "", "accessPrivate", "", "myClass", "Lcom/example/java_test/MyClass;", "create", "java_test_debug"}
   )
   public static final class Factory {
      @NotNull
      public final MyClass create() {
         String var1 = "Creating MyClass instance";
         System.out.println(var1);
         return new MyClass();
      }

      public final void accessPrivate(@NotNull MyClass myClass) {
         Intrinsics.checkNotNullParameter(myClass, "myClass");
         String var2 = myClass.instanceProperty;
         System.out.println(var2);
      }

      private Factory() {
      }

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

为什么不是真正的 static

  • 伴生对象的本质是一个类级别的对象实例,而非真正的静态成员。每个类仅有一个伴生对象实例;
  • Java static 静态成员直接属于类,不依赖任何实例,在编译期生成静态方法表;Kotlin 半生对象,编译后生成一个真实的类,并持有外部类的引用;

3 对象表达式(Object Expressions)------ 替代匿名内部类

创建一个匿名类的匿名对象,常用于实现接口或继承类(可实现多个接口或继承类)。

kotlin 复制代码
interface ClickListener {
    fun onClick()
}

abstract class KeyAdapter {
    open fun keyPress() {

    }
}

fun main() {
    val count = 0 // val 保证不可变,可以被对象表达式捕获

    // 创建一个实现 ClickListener 接口的匿名对象
    val myButtonListener = object : ClickListener {
        override fun onClick() {
            println("Button clicked! Count was $count")
        }
    }

    myButtonListener.onClick()

    // 创建一个继承 KeyAdapter 并实现 ClickListener 的匿名对象
    val myKeyHandler = object : KeyAdapter(), ClickListener {
        override fun keyPress() {
            println("Key pressed!")
        }

        override fun onClick() {
            println("key handler also handles clicks?")
        }

    }
}

4 总结

  • 单例模式:object Singleton 简化线程安全的单例实现;
  • 静态成员:companion object 替代 Java 中的 static 关键字;
  • 匿名类:object : Interface { ... } 替代 Java 中的匿名内部类;
相关推荐
Dxy12393102162 分钟前
python如何将word的doc另存为docx
开发语言·python·word
CodeWithMe12 分钟前
【软件开发】什么是DSL
开发语言
IT_102412 分钟前
springboot从零入门之接口测试!
java·开发语言·spring boot·后端·spring·lua
我命由我1234534 分钟前
VSCode - VSCode 转换英文字母的大小写
开发语言·javascript·ide·vscode·编辑器·html·软件工具
什么半岛铁盒1 小时前
Linux中INADDR_ANY详解
开发语言·c++·算法
m0_516484671 小时前
C#引用传递代码记录
开发语言·c#
zh_xuan2 小时前
c++ std::function
开发语言·c++
AgilityBaby2 小时前
Untiy打包安卓踩坑
android·笔记·学习·unity·游戏引擎
hunzi_12 小时前
PHP商城源码:构建高效电商平台的利器
开发语言·php