一个具有伴生对象的类和一个类和对象名称相同的类之间有什么区别?

13

Scala类的“伴生对象”可以看作是具有与类相同的完全限定名称(即相同的名称,在相同的包中)的单例对象。它们用于保存对于类的所有实例都通用的实用函数,作为Java中static方法的替代。

然而,在文档和问题的各个地方,它说伴生对象必须在同一编译单元中定义。例如,它们必须在同一文件中定义;不能为Java对象定义伴生对象在REPL中,它们必须在同一输入行中定义,因此会出现警告消息:

warning: previously defined class Foo is not a companion to object Foo.
Companions must be defined together; you may wish to use :paste mode for this.

这意味着类和其伴生对象之间必须有区别,而只是具有相同(完全限定)名称的类和对象则不然。这个区别是什么?
1个回答

17

我们称这个类为 class SomeClass (虽然它也可以是例如一个 trait)。

私有成员

伴生对象(object SomeClass)的方法可以访问 class SomeClass实例的私有方法/数据。

如果您的伴生对象仅使用类的公共接口(例如仅定义常量),则没有实际区别。但有许多情况下,让实用函数访问私有成员非常有用。例如,object SomeClass 可以定义一个工厂方法apply,用于设置class SomeClass的私有成员,而无需在公共接口中公开设置器。在这种情况下,必须通过将object SomeClass的定义放在与class SomeClass相同的编译单元中来定义伴生对象。

另一个区别是编译器会在类型(和其超类型)的伴生对象中搜索隐式值。因此,如果您正在使用在class SomeClass代码中定义的隐式转换,则必须在伴生对象中定义它们。

注释

这两者的组合也解释了同一编译单元限制。

  • scalac 在不知道class SomeClass调用的私有成员之前无法编译object SomeClass
  • scalac 在不知道class SomeClass调用的隐式转换之前不能编译class SomeClass。因此,伴生对象必须在 class SomeClass 之前编译。

因此它们必须同时编译。此外,当前编译器显然会分别编译不同的文件(请参见不支持将类拆分为多个文件),因此限制到同一编译单元。


2
“同一编译单元”限制不仅是编译器的实现细节或编译器工程师的懒惰,它还是证明您有权访问类的私有成员(即打破封装)的有用方法:如果您无论如何都可以编辑文件,那么封装已经不存在了,因此通过将它们暴露给伴生对象来公开私有成员不会损失任何安全性。如果任何人随时随地都可以定义伴生对象,他们可以轻松地通过向它们添加伴生对象来打破第三方类的封装。 - Jörg W Mittag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接