You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin伴生对象实现单例时为何检测到存在公共构造函数

问题现象

我在结合设计模式学习Kotlin时,尝试编写单例模式实现,配套两组测试用例:一组验证单例实例全局唯一,一组验证单例类不存在公共构造函数,测试代码如下:

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertSame
import org.junit.jupiter.api.Test

class TestSimpleSingleton {
    @Test
    fun testCreateSingleton() {
        val s = Singleton.Instance()
        val s2 = Singleton.Instance()
        assertSame(s, s2)
    }

    @Test
    fun testNoPublicConstructors() {
        val singleton = Class.forName("Singleton")
        val constructors = singleton.constructors
        assertEquals(0, constructors.size) // public constructor = 0
    }
}

两种实现的测试表现:

  • 直接使用object关键字实现单例(调用方式val s = Singleton)时,测试全部通过,实现代码:
object Singleton
  • 使用companion object(伴生对象)实现懒加载单例时,第二个测试失败,检测到类存在1个公共构造函数,实现代码:
class Singleton private constructor(){
  companion object {
    private var theInstance: Singleton? = null
    fun Instance(): Singleton {
      if (theInstance == null)
        theInstance = Singleton()
      return theInstance as Singleton
    }
  }
}

我已经将类的主构造函数明确声明为private,理论上不应存在公共构造函数。

产生原因

这个问题是JVM平台上Kotlin编译器的合成构造函数生成规则导致的:

  • 你定义的private constructor()无参主构造,编译后确实是private访问权限,正常业务代码无法直接调用。
  • 当类中存在伴生对象时,Kotlin编译器会额外生成一个标记为*synthetic(合成)*的public构造函数,参数是编译器内部使用的DefaultConstructorMarker类型。这个构造是给编译器内部逻辑调用的,普通代码无法直接调用,但它的访问权限是public,因此会被JDK反射的getConstructors()方法(也就是你代码中调用的constructors扩展属性的底层实现)识别到,统计为1个公共构造。
  • 而用object关键字声明的单例类,编译器不会生成这个额外的合成public构造,只有一个private的无参构造,因此反射统计到的公共构造数量为0,测试可以通过。
修正方案

如果要准确判断类不存在用户可调用的公共构造函数,需要在反射统计时过滤掉编译器生成的合成构造,修改测试代码如下:

@Test
fun testNoPublicConstructors() {
    val singleton = Class.forName("Singleton")
    val constructors = singleton.constructors.filter { !it.isSynthetic } // 过滤编译器生成的合成构造
    assertEquals(0, constructors.size)
}

另外补充:你当前手写的懒汉单例是线程不安全的,多线程并发场景下可能生成多个实例,Kotlin中推荐用by lazy委托实现线程安全的懒加载单例,代码更简洁:

class Singleton private constructor(){
  companion object {
    val Instance: Singleton by lazy { Singleton() }
  }
}

内容的提问来源于stack exchange,提问作者blahblah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:33:17