Java: не удается получить доступ к защищенным методам подкласса вложенного класса в подклассе внешнего класса

Я разлагаю класс, чтобы обеспечить повторное использование, различные реализации со скрытой информацией и т. д.

Это было:

public class Outer
{
    public static class Inner
    {
        // ...
        protected static void innerDoSomething()
        {
            // ...
        }
    }

    public outerDoSomething()
    {
        //...   
        Inner.innerDoSomething();
        //...
    }
}

И все было хорошо, так как внешнему классу Outer разрешен доступ к защищенным членам вложенного класса Inner.

Но пытаюсь разложить как таковой:

public class SuperOuter
{
    public static class SuperInner
    {
        // ...
        protected static void innerDoSomething()
        {
            // ...
        }
    }

    // ...
}

public class SubOuter extends SuperOuter
{
    public static class SubInner extends SuperInner
    {
        // ...
        protected static void innerDoSomethingElse()
        {
            // ...
        }
    }

    public outerDoSomething()
    {
        //...   
        SubInner.innerDoSomethingElse(); // OK
        SubInner.innerDoSomething();     // Error: cannnot access!
        //...
    }
}

innerDoSomething() недоступен, даже если защищенные члены SubInner доступны для SubOuter, и все защищенные члены SuperInner должны быть частью защищенного интерфейса SubInner.

Кажется, единственный способ заставить его работать — добавить явное делегирование для каждого метода, например:

    public static class SubInner extends SuperInner
    {
        // ...
        protected static void innerDoSomethingElse()
        {
            // ...
        }

        protected static void innerDoSomething()
        {
            SuperInner.innerDoSomething();
        }
    }

Что раздражает, подвержено ошибкам и тратит время. У меня возникает соблазн просто объявить innerDoSomething() общедоступным, но это не совсем правильно, поскольку он предназначен для использования только SubOuter и его подклассами.

Почему? Не должен ли защищенный доступ innerDoSomething() быть унаследован и доступен для SubOuter?

почему все static?

luk2302 18.02.2019 16:50

Я полагаю, что в этой ситуации компилятор фактически назначает Супервнутренний внешним родителем субвнутренний, что отрывает область действия от SubOuter. Если вы удалили расширение, оно должно работать.

Dylan 18.02.2019 16:52

Я также хотел бы добавить, что это не соответствует никаким официальным шаблонам декомпозиции ООП. Ваша методика очень странная. Я считаю, что, вероятно, есть более правильный способов сделать то, что вы пытаетесь сделать.

Dylan 18.02.2019 16:54

@Dylan В примере кода вообще не показан шаблон декомпозиции. Мне просто нужно разложить как Outer, так и Inner, поскольку оба содержат детали, которые должны быть указаны по-разному в разных подклассах.

GozzoMan 18.02.2019 17:47
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
952
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Защищенный метод не может быть доступен из другого пакета через ссылку на переменную. Это будет работать, если два класса SuperOuter и SubOuter будут находиться в одном пакете.

Похоже, это действительно так: SuperOuter и SubOuter были в разных пакетах, и перемещение SubOuter в один и тот же пакет было решено волшебным образом. Теперь, по логике вещей, SuperOuter и SubOuter должны находиться в разных пакетах. Я предполагаю, что реальная исходная проблема заключается в том, что доступ к защищенному члену вложенного класса предоставляется как специальное средство доступа для внешнего класса, тогда как на самом деле это специальное средство доступа для классов в том же пакете.

GozzoMan 18.02.2019 17:36
Ответ принят как подходящий

Хорошо, я думаю, что обошёл это, внимательно перечитав Язык программирования Java, 4-е изд. Гослинга:

  1. SuperOuter и SubOuter находятся в разных пакетах, как и должно быть по логике, поэтому существует доступ к защищенным членам без одеяла.

  2. Доступ к защищенному члену вложенного класса на самом деле предназначен как специальное средство доступа между внешними и вложенными классами (см. Глава 5. Вложенные классы и интерфейсы)

  3. Тем не менее, доступность защищенного члена не просто транзитивно: кроме формы вложенного/внешнего гранта, вам нужно, чтобы оба находились в подклассе и иметь ссылку, по крайней мере, типа этого подкласса (т.е. как минимум SubXXX, SuperXXX недостаточно), потому что, цитирую абзац 3.5. Что на самом деле означает защита:

Each subclass inherits the contract of the superclass and expands that contract in some way. Suppose that one subclass, as part of its expanded contract, places constraints on the values of protected members of the superclass. If a different subclass could access the protected members of objects of the first subclass then it could manipulate them in a way that would break the first subclass's contract and this should not be permissible.

Таким образом, даже если в моем случае xxxInner логически должны быть частью xxxOuter, а SubOuter расширяет SuperOuter, поэтому первый логически должен иметь доступ к любым защищенным членам второго, тем не менее SubOuter не может получить доступ к защищенным членам SuperInner в SuperOuter только в точно так же, как если бы он получил SuperInner в качестве аргумента параметра потому что этот аргумент может относиться к совершенно другому разветвлению иерархии. В этом случае в языке нет специального положения для установления связи.

Это также объясняет, почему работает явное делегирование: SubOuter имеет доступ к защищенным членам SubInner, потому что внешний/вложенный грант, а SubInner имеет доступ к защищенным членам SuperInner из-за расширения, но SubOuter не может получить доступ к защищенным членам SuperInner, потому что последние могут фактически принадлежать к другому разветвлению иерархии и, так сказать, это работа SubInner, чтобы установить соединение.

Явное делегирование учитывает все вышеперечисленное, логически и в соответствии с определениями языка, поэтому это должен быть «правильный» путь, учитывая надлежащий доступ, но он подвержен ошибкам в реализации из-за избыточности (что, если я вызову SuperInner.innerDoSomethingElse() внутри SubInner.innerDoSomething()?)

В конечном счете: я мог бы, например, автоматизировать явное делегирование с помощью некоторых сценариев Python (я сделал это, чтобы автоматизировать создание шаблонов Builder, которые имеют аналогичную слабость избыточности), или просто отказаться от дополнительной безопасности контроля доступа и сделать защищенные члены общедоступными.

Другие вопросы по теме