В чем разница между классом с сопутствующим объектом и классом и объектом с тем же именем?

«Компаньон-объект» класса 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.

Это означает, что должно быть различие между классом с сопутствующим ему объектом и просто классом и объектом с одинаковым (полным) именем. Что это за различие?


person Mechanical snail    schedule 22.07.2012    source источник


Ответы (1)


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

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

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

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

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

Комментарии

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

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

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

person Mechanical snail    schedule 22.07.2012
comment
То же самое ограничение единицы компиляции — это не просто деталь реализации компилятора или лень со стороны инженеров-компиляторов. Это также полезный способ доказать, что у вас есть право доступа к закрытым членам класса, т. е. нарушить инкапсуляцию: если вы все равно можете редактировать файл, то инкапсуляция уже вне окна, так что вы не теряете никакой безопасности. подвергая их сопутствующему объекту. Если бы кто-то мог определить сопутствующие объекты в любое время и в любом месте, он мог бы тривиально нарушить инкапсуляцию сторонних классов, просто добавив к ним объект-компаньон. - person Jörg W Mittag; 05.11.2014