1. 概述

在本篇教程中,我们将深入探讨 Kotlin 中 类(class)对象(object) 的区别,尤其是它们在语言层面和字节码层面的不同表现。

我们会通过具体代码和反编译分析,来帮助我们理解其背后的设计机制。


2. 类的表示方式

Kotlin 中的类与 Java 类非常相似:它们是用于创建实例的模板。

class Person {
    // 省略内容
}

在编译后,Kotlin 编译器会生成一个标准的 Java 字节码类,例如:

>> kotlinc Person.kt
>> javap -c -p -v com.baeldung.classobject.Person
public final class com.baeldung.classobject.Person
  // omitted
  flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
  this_class: #2        // com/baeldung/classobject/Person
  super_class: #4       // java/lang/Object
  interfaces: 0, fields: 0, methods: 1, attributes: 2

注意: Kotlin 类默认是 final 的,这意味着它们不能被继承,除非你显式使用 open 关键字。

我们可以像 Java 那样从具体类中创建实例:

val p = Person()

同时,只要类是 open 的,我们也可以继承它:

open class Person {
    // 省略内容
}

class User : Person() {
    // 省略内容
}

3. 单例对象(Singleton Objects)

在某些场景下,我们希望某个类只能有一个实例存在,这就是所谓的 单例模式。在 Kotlin 中,我们可以通过 object 声明来实现这一点:

object FileSystem {
    
    fun createTempFile() {
        // 省略实现
    }
}

由于 FileSystem 是一个单例对象,它的成员可以像 Java 中的静态成员一样被访问,无需实例化

fun main() {
    FileSystem.createTempFile()
}

不能通过构造函数创建实例

val f = FileSystem() // ❌ 编译错误

可以实现接口或继承类

object FileSystem : Serializable {
    // 省略内容
}

但不能被继承

class NTFS : FileSystem() // ❌ 编译错误

3.1. 字节码表示

在底层,Kotlin 编译器会将 object 编译成一个 final 类,并且构造函数是 private 的:

>> kotlinc FileSystem.kt 
>> javap -c -p com.baeldung.classobject.FileSystem
public final class com.baeldung.classobject.FileSystem {
  private com.baeldung.classobject.FileSystem();
    Code:
       0: aload_0
       1: invokespecial #11    // Method java/lang/Object."<init>":()V
       4: return
}

它通过一个 static final 变量来持有该单例:

public final class com.baeldung.classobject.FileSystem {
  public static final com.baeldung.classobject.FileSystem INSTANCE;

  // omitted
}

初始化时,Kotlin 会生成一个静态代码块来创建实例并赋值:

static {};
    Code:
       0: new           #2    // class com/baeldung/classobject/FileSystem
       3: dup
       4: invokespecial #26   // Method "<init>":()V
       7: astore_0
       8: aload_0
       9: putstatic     #28   // Field INSTANCE:Lcom/baeldung/classobject/FileSystem;
      12: return

这与 Java 中常见的单例实现方式非常类似。


4. 总结

本文我们从语言层面和字节码层面对比了 Kotlin 中类与对象的区别,总结如下:

  • ✅ 类可以被实例化,而对象是单例,不能被实例化
  • ✅ 类可以被继承(前提是 open),而对象不能被继承
  • ✅ 对象的底层实现是通过私有构造器 + 静态实例完成的,类似于 Java 单例

如果你希望设计一个全局唯一实例的组件,使用 object 是最简洁、安全的方式。

如需查看完整示例代码,可以访问 GitHub 仓库


原始标题:Difference Between Classes and Singleton Objects in Kotlin