同一模块中全局变量与object声明的区别是什么?
你的理解有部分正确——两者在当前全局变量/object的实例唯一性上表现类似,但它们在语义、实例控制、扩展性等核心层面有本质区别,具体差异如下:
实例唯一性的保障程度不同
全局val的方式只是创建了一个全局的EventLog实例,但EventLog类本身仍然是普通类,任何地方都可以通过EventLog()创建新的实例:// 完全合法,会生成新的EventLog实例 val anotherEventLog = EventLog()而
object声明的类是真正的单例,它的构造器被自动设为私有,无法在任何地方创建新实例,整个程序生命周期内只有这一个实例。语义明确性不同
object声明是Kotlin专门为单例场景设计的语法,看到它就明确知道这是一个全局唯一的单例,语义清晰无歧义;而全局val的方式只是一个普通的全局变量指向类实例,阅读代码的人无法直接判断这个实例是否是程序中唯一的EventLog实例。扩展性不同
普通类EventLog可以被继承,也可以实现多个接口:class EventLog : BaseLog(), Loggable { // ... }但
object声明的类是final的,无法被继承(不过可以实现接口),如果后续需要基于该类扩展子类,object方式无法满足需求。序列化行为不同
object声明的单例在序列化时,Kotlin会自动处理实例唯一性,反序列化后得到的仍然是原来的单例实例;而全局val的普通类实例,默认序列化后反序列化会生成新的实例,需要自己额外实现单例的序列化逻辑(比如添加readResolve()方法)才能保证唯一性。初始化细节差异
顶层的全局val实例是在首次被访问时懒加载初始化;object声明的实例同样是懒加载,但它的初始化是线程安全的(Kotlin自动实现了双重校验锁的线程安全逻辑),而顶层val的线程安全则依赖于JVM对顶层属性的初始化保障(本质也是线程安全的,但语义上object的线程安全更明确)。
内容的提问来源于stack exchange,提问作者Alexander Petrov

