Java: вызов защищенного метода суперкласса из подкласса - не виден?

Я вызываю защищенный метод суперкласса из подкласса. Почему этот метод "не виден"?

Я читал некоторые сообщения, такие как этот, которые кажутся противоречить следующему:

Суперкласс:

package com.first;

public class Base
{
    protected void sayHello()
    {
        System.out.println("hi!");
    }
}

Подкласс:

package com.second;

import com.first.Base;

public class BaseChild extends Base
{
    Base base = new Base();

    @Override
    protected void sayHello()
    {
        super.sayHello(); //OK :)
        base.sayHello(); //Hmmm... "The method sayHello() from the type Base is not visible" ?!?
    }   
}

person rapt    schedule 24.03.2016    source источник
comment
stackoverflow.com/a/19949354/868975   -  person Bax    schedule 24.03.2016
comment
Возможно, такой же, как этот: доступ к защищенному члену статического класса в подклассе родительского% 23comment59830891_36093187"> stackoverflow.com/questions/36093187/   -  person aioobe    schedule 24.03.2016


Ответы (3)


base — это переменная, которая ничем особым не отличается: она не является частью иерархии классов и защищенный доступ через нее недоступен. Несмотря на то, что sayHello имеет доступ к защищенным членам Base, он имеет этот доступ только через наследование (поскольку он не находится в одном и том же пакете: ключевое слово protected разрешает доступ как через наследование, так и через пакет, см. таблицу в это руководство по Oracle).

Доступ через this и super разрешен, поскольку они являются частью иерархии наследования.

person Paul Hicks    schedule 24.03.2016
comment
Я думаю, вы единственный здесь, кто заметил, что base была просто переменной. - person Scary Wombat; 24.03.2016
comment
Я думаю, что мне нужна лучшая терминология. Я знаю, что пытаюсь сказать, но я думаю, что есть более общепринятые термины, которые я сейчас не могу вспомнить. - person Paul Hicks; 24.03.2016
comment
Это все еще удивительно, поскольку вы можете вызывать частные методы для любого экземпляра объявленного в данный момент класса (this и любого экземпляра, отличного от this). Работает следующий код: public class A { private void b() { new A().b(); }} - person Stefan Dollase; 24.03.2016
comment
@StefanDollase, но это внутри A. Конечно, частные методы A доступны A. - person ChiefTwoPencils; 24.03.2016
comment
@ChiefTwoPencils Да, но это все тот же аргумент: ответ говорит, что он не работает, потому что это та же иерархия наследования, но не тот же экземпляр. Я показал, что в случае с модификатором private достаточно находиться в той же иерархии наследования (состоящей только из самого типа). Для private не имеет значения, является ли это одним и тем же экземпляром. Я лишь указал, что меня удивляет расходящееся поведение. - person Stefan Dollase; 24.03.2016
comment
Разница в том, что это один и тот же класс. Подкласс не задействован. См. цитату dasblinkenlight для получения более подробной информации. - person Paul Hicks; 24.03.2016
comment
@StefanDollase, я прочитал это по-другому. Звучит правильно и не так, как ваш пример. Основное отличие заключается в том, что OP пытается использовать экземпляр родителя, как если бы он был this. Если бы это было в другом классе, то да, это было бы очень удивительно. - person ChiefTwoPencils; 24.03.2016
comment
Просто для ясности: меня удивляет не поведение private, а поведение protected. Я ожидал, что модификатор protected ведет себя аналогично модификатору private в отношении ситуации, описанной выше. Однако я, кажется, единственный, кто удивлен, так что не обращайте внимания :-P - person Stefan Dollase; 24.03.2016
comment
@ChiefTwoPencils Но как бы вы получили доступ к личным вещам в другом классе? Я согласен со Стефаном Долласом в том, что это несколько непоследовательно. - person rapt; 24.03.2016
comment
@rapt, это несовместимо по замыслу. Вы не получаете доступ к личным материалам (напрямую или без магии) в другом классе, независимо от того, унаследованы они или иным образом; тогда это не было бы частным. Если то, как это работает, идет вразрез с вашим дизайном, то, возможно, над дизайном нужно подумать. - person ChiefTwoPencils; 24.03.2016
comment
@ChiefTwoPencils Я не говорю о доступе к личным вещам из другого типа. Я думаю, вы все еще упустили мою мысль: для private вам нужно находиться в том же объявляющем классе, но вы можете использовать private материал из других экземпляров, чем this. Для protected вы должны находиться в той же иерархии классов, что и объявляющий класс, и вы можете только использовать protected элементы из this. - person Stefan Dollase; 24.03.2016
comment
@StefanDollase, извините, я понимаю, что вы говорите, но не вижу, что в этом удивительного. Даже если у вас был другой экземпляр класса, вы все равно находитесь в том же классе. Хотя это может показаться непоследовательным, и, возможно, по определению так оно и есть, в этом есть смысл (для меня). Помимо нахождения внутри класса, который гарантирует, что конфиденциальность не будет нарушена, некоторые вещи, такие как рекурсивные определения, могут быть невозможны или излишне сложны. Подумайте о BinaryTreeNode, у которого не было бы доступа к закрытым материалам, хранящимся в его ссылках. Это было бы шокирующим для меня. Все мнения :) - person ChiefTwoPencils; 24.03.2016
comment
@ChiefTwoPencils Как я уже сказал, меня удивляет не поведение private, а поведение protected. Таким образом, вопрос не в том, почему это разрешено для частного, а в том, почему это не разрешено для защищенного. Давайте перенесем ваш пример алгоритма, использующего закрытые методы на экземплярах, отличных от this, на защищенную версию: Почему я не могу реализовать алгоритм, использующий защищенные методы из текущей иерархии наследования, но на экземпляре, отличном от this? Почему я не могу получить доступ к защищенным методам родительского класса дочерних узлов в BinaryTreeNode? - person Stefan Dollase; 24.03.2016
comment
@ChiefTwoPencils Я остановлюсь на этом здесь, так как это тема для другого вопроса. Ваше здоровье! - person Stefan Dollase; 24.03.2016
comment
@Стефан, для протокола, я разговариваю по телефону и пропустил твой уточняющий комментарий. Извините, до следующего раза... - person ChiefTwoPencils; 24.03.2016

Это правильное поведение. Фактически, Спецификация языка Java, раздел 6.6.2-1, содержит пример, очень похожий на ваш, с комментарием о том, что его не следует компилировать.

Особенности доступа к защищенным членам подробно описаны в разделе 6.6.2.1:

6.6.2.1. Доступ к защищенному участнику

Пусть C будет классом, в котором объявлен защищенный член. Доступ разрешен только внутри тела подкласса S из C.

Кроме того, если Id обозначает поле экземпляра или метод экземпляра, то:

Если доступ осуществляется по полному имени Q.Id, где Q — это ExpressionName, то доступ разрешается тогда и только тогда, когда тип выражения Q равен S или подклассу S.

Если доступ осуществляется выражением доступа к полю E.Id, где E является первичным выражением, или выражением вызова метода E.Id(. . .), где E является первичным выражением, то доступ разрешается тогда и только тогда, когда тип E равен S или подкласс S.

Это последний абзац, который описывает, почему доступ должен быть запрещен. В вашем примере C — это Base, S — это BaseChild, а E, тип переменной base, также равен Base. Поскольку Base не является ни BaseChild, ни подклассом BaseChild, доступ запрещен.

person Sergey Kalinichenko    schedule 24.03.2016
comment
Хотя я понимаю, что это предотвращает доступ через base, я не понимаю, как он разрешает доступ через super. Разве super не того же типа, что и base? Может я что-то пропустил в JLS? Разрешено ли это в другой части JLS? Я не смог найти раздел, который разрешает это. - person Stefan Dollase; 24.03.2016
comment
@StefanDollase В том-то и дело, что super - это не поле, это ключевое слово, которое позволяет вам получить доступ к методам базового класса, переопределенным в вашем классе. Однако фактическая ссылка осуществляется через this, а не через другую переменную. - person Sergey Kalinichenko; 24.03.2016

Клавиатура защищена предназначена для видимости в том же пакете. Если дочерний класс не относится к тому же пакету, защищенные методы родителя не видны дочернему классу.

person Dust Francis    schedule 24.03.2016
comment
Чтобы объяснить отрицательные голоса: клавиатура — это устройство для ввода символов в компьютер. protected предназначено для видимости в том же пакете - в том же пакете, но я отвлекся. protected в основном используется для предоставления доступа к подклассам. Если дочерний класс не из того же пакета - в том же... защищенный метод родителя не виден дочернему классу. Нет. Доступ из подкласса — это весь смысл protected. Вероятно, вы имеете в виду видимость по умолчанию, которую включает доступ protected. - person Johannes Kuhn; 15.07.2018