Подтвердить что ты не робот

Объект-компаньон для частного класса: почему он не действителен?

Мне нужны два экземпляра, которые имеют доступ друг к другу рядовых. Я естественно думал о сопутствующем объекте, который предоставляет доступ к одному и только экземпляру этого класса-компаньона. сам класс я сделал частным, поэтому пользователи не могут просто создавать экземпляры с помощью new.

object A {
    def apply = dual
    lazy val dual = new A
}

private class A {
    //some irrelevant logic...
}

этот код не компилируется. я get: класс A избегает своей определяющей области как часть ошибки типа A, которую я действительно не понимаю. моим текущим обходным путем было определение черты с каждым объявлением метода, которое должен иметь класс, и сделать class A расширением этой черты, в то время как двойным является тип признака, а не тип class A.

Какую теоретическую проблему мне не хватает? почему этот запрет?

4b9b3361

Ответ 1

Решение Paolo хорошее (+1), но он не объяснил сообщение об ошибке, поэтому позвольте мне попробовать. Проблема связана с тем, что каждому методу нужен тип возврата. Исходное определение apply и dual возвратило объект class A, поэтому неявный возвращаемый тип был A. Это означает, что A должен быть видимым для клиентов - как иначе они могли бы вызвать функцию или получить доступ к val? Более того, поскольку оба - и их родительский объект тоже - являются общедоступными, они глобально видны. Однако вы объявили A private, что означает, что он не должен быть виден вне его пакета. Таким образом, существует конфликт, который не может быть разрешен компилятором.

Общее правило заключается в том, что все параметры и возвращаемые типы функций/членов должны иметь (по крайней мере) ту же область видимости, что и сам ссылочный элемент *. Таким образом, одним из тривиальных способов решения этой проблемы было бы уменьшить видимость apply и dual до private. Это удовлетворило бы компилятор, но не вы: -)

Ваше решение устраняет проблему, изменяя статический тип возвращаемого значения на public признак, который, таким образом, имеет ту же видимость, что и члены, ссылающиеся на него. Динамический тип возвращаемого объекта все еще class A, однако это не должно быть видимым для клиентов. Это классический пример принципа "для интерфейсов, а не для реализации" .

Обратите внимание, что для полного применения этого принципа можно превратить class A в внутренний класс private object A, что делает его недоступным даже для других классов внутри одного пакета:

trait A {
    //...
}

object A {
    def apply: A = dual
    lazy val dual: A = new AImpl

    private class AImpl extends A {
        //some irrelevant logic...
    }

}

* Чтобы быть педантичным, охватывающий класс/объект может уменьшить видимость его элементов, например здесь:

private class Holder {
  def member = new Hidden
}

private class Hidden

, где member есть public, но его охватывающий класс private, эффективно скрывая его члены от внешнего мира. Поэтому компилятор не подает никаких жалоб здесь.

Ответ 2

Я думаю, что вам не нужен частный класс, а класс с частным конструктором.

class A private() 
object A {
    def apply = dual
    lazy val dual = new A
}

Теперь ваш класс является "видимым" для внешнего кода, но только ваш объект-компаньон может создавать экземпляры его.