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

伴生对象中const val与val的实现及字节码差异详解

Kotlin伴生对象中const val与val的内部实现及字节码差异

除了const val是编译时常量这一核心区别外,二者在内部实现、字节码转换和实际使用上还有以下具体差异:

1. 字节码层面的存储与访问逻辑

  • const val:
    会被直接编译为宿主类的静态常量字段,字节码标记为public static final,值直接存储在宿主类的常量池中。不需要生成getter方法,访问时直接读取常量值,没有任何间接调用。
    用javap查看字节码,宿主类中会直接出现:

    public static final java.lang.String a;
    

    并且在类初始化阶段就完成赋值。

  • val:
    实际存储在伴生对象的实例字段中,字节码里伴生对象类会生成一个private final的实例字段,同时宿主类会生成一个静态的getter方法(如getA()),内部通过伴生对象的单例实例间接访问该字段。
    对应的关键字节码:

    • 宿主类中存在静态的伴生对象实例:public static final YourClass$Companion Companion;
    • 伴生对象类中:private final java.lang.String a; 和 public java.lang.String getA();
    • 宿主类的静态getter:public static java.lang.String getA() { return Companion.getA(); }

2. Java代码中的调用方式差异

  • const val:Java代码可以直接通过宿主类.a访问,和Java原生静态常量的调用方式完全一致。
  • val:默认情况下Java必须通过宿主类.Companion.getA()调用;只有给该val添加@JvmStatic注解后,才能像宿主类.getA()这样直接调用,但本质上还是编译器生成了静态转发方法。

3. 初始化时机与计算逻辑

  • const val:值在编译阶段就已确定,类加载时直接从常量池加载,不存在运行时计算逻辑。只能用编译期可确定的字面量或常量表达式赋值(比如const val b = a + " there",前提a是const val)。
  • val:属于运行时初始化,伴生对象的单例实例在宿主类第一次被加载或访问时创建,字段值在此时赋值。支持运行时计算的表达式(比如val a = generateHiString()),这些逻辑会在伴生对象初始化时执行。

4. 字段可见性的底层差异

  • const val:默认是public的静态常量,直接暴露给外部,字节码中没有访问控制的间接层。
  • val:伴生对象中的字段默认是private final,外部只能通过生成的getter方法访问,相当于封装了一层,即使在Kotlin代码中直接写YourClass.a,底层也是调用getter方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:06:24