具有伴随对象的类与具有相同名称的类和对象之间的区别是什么?

Mec*_*ail 13 oop scala language-lawyer scala-java-interop companion-object

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.
Run Code Online (Sandbox Code Playgroud)

这意味着必须区分具有其伴随对象的类,以及具有相同(完全限定)名称的类和对象.这是什么区别?

Mec*_*ail 16

我们打电话给班级class SomeClass(虽然它也可以是例如a trait).

私人会员

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

如果你的伴侣对象只使用你的类的公共接口(例如,只定义常量),那就没有实际的区别.但是在许多情况下,让实用程序函数访问私有成员是有用的.例如,object SomeClass可以定义一个apply设置私有成员的工厂方法class SomeClass,而不必在公共接口中公开setter.因此,在这种情况下,必须通过将定义object SomeClass放在同一个编译单元中来定义伴随对象class SomeClass.

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

评论

两者的结合也解释了相同的编译单元限制.

  • scalacobject SomeClass直到知道class SomeClass它调用的私有成员才能编译.
  • scalacclass SomeClass直到它知道它调用的含义才能编译.因此必须在不迟于编译伴随对象class SomeClass.

因此必须同时编译它们.此外,当前编译器显然单独编译单独的文件(参见缺少对跨多个文件拆分类的支持),将其限制为相同的编译单元.

  • “相同的编译单元”限制不仅是编译器的实现细节,还是部分编译器工程师的懒惰。这也是证明您有权访问该类的私有成员(即中断封装)的一种有用方法,即:无论如何您都可以编辑文件,那么封装已经不存在了,因此您不会失去任何安全性通过将它们暴露于伴随对象。如果有人可以随时随地定义同伴对象,则只需向其添加同伴对象,就可以轻易破坏第三方类的封装。 (2认同)