Какая разница между классом со компаньоном и классом и объектом с тем же именем?

A 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

Позвольте вызвать класс class SomeClass (хотя он может также быть, например, a trait).

Частные члены

Способы объекта-компаньона (object SomeClass) имеют доступ к закрытым методам/данным экземпляров class SomeClass.

Если ваш объект-компаньон использует только открытый интерфейс вашего класса (например, просто определяет константы), нет никакой практической разницы. Но есть ряд случаев, когда полезно, чтобы функции полезности получали доступ к закрытым членам. Например, object SomeClass может определить метод factory apply, который устанавливает частные члены class SomeClass, не подвергая сеттерам в открытом интерфейсе. В таких случаях вы должны определить объект-компаньон, поставив определение object SomeClass в том же компиляционном блоке, что и class SomeClass.

Другое отличие состоит в том, что компилятор ищет implicits в сопутствующих объектах типа (и его супертипов). Поэтому, если вы используете неявные преобразования, которые вы определяете в коде class SomeClass, вы должны определить их в сопутствующем объекте.

Комментарии

Комбинация этих двух также объясняет одно и то же ограничение единицы компиляции.

  • scalac не может скомпилировать object SomeClass, пока не узнает, какие частные члены class SomeClass он вызывает.
  • scalac не может скомпилировать class SomeClass, пока не узнает, какие вызовы он вызывает. Поэтому объект-компаньон должен быть скомпилирован не позднее class SomeClass.

Следовательно, они должны быть скомпилированы в одно и то же время. Кроме того, текущий компилятор, по-видимому, компилирует отдельные файлы отдельно (например, отсутствие поддержки для разделения классов по нескольким файлам), ограничивая его одним и тем же компилятором.