Java: защищенный доступ через пакеты

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

Я знаю для классов вне пакета, подкласс может видеть защищенный член только через наследование.

Существует два пакета: package1 и package2.

  • package1: ProtectedClass.java

    package org.test.package1;
    
    public class ProtectedClass {
    
        protected void foo () {
            System.out.println("foo");
        }
    }
    
  • package2: ExtendsprotectedClass.java

    package org.test.package2;
    
    import org.test.package1.ProtectedClass;
    
    public class ExtendsprotectedClass  extends ProtectedClass {
    
        public void boo() {
            foo(); // This works, 
                   // since protected method is visible through inheritance
        }
    
        public static void main(String[] args) {
            ExtendsprotectedClass epc = new ExtendsprotectedClass();
            epc.foo(); // Why is this working? 
                       // Since it is accessed through a reference,
                       // foo() should not be visible, right?
        }
    }
    
  • package2: UsesExtendedClass.java

    package org.test.package2;
    
    public class UsesExtendedClass {
    
        public static void main(String[] args) {
            ExtendsprotectedClass epc = new ExtendsprotectedClass();
            epc.foo(); // CompilationError: 
                       // The method foo() from the type ProtectedClass
                       // is not visible
        }
    }
    

Понятно, что метод boo() в ExtendsprotectedClass может получить доступ к foo(), так как защищенные члены могут быть доступны только через наследование.

Мой вопрос: почему метод foo() работает нормально при доступе через ссылку в методе main() ExtendsprotectedClass, но не будет работать при обращении через ссылку epc в UsesExtendedClass?

Ответы

Ответ 1

Код в классе ExtendsprotectedClass имеет доступ к защищенным членам ProtectedClass через ссылку типа ExtendsprotectedClass. Из JLS раздел 6.6.2:

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

и

Пусть C - класс, в котором объявлен защищенный член m. Доступ разрешен только внутри тела подкласса S of C. Кроме того, если Id обозначает поле экземпляра или метод экземпляра, то:

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

UsesExtendedClass не является неприемлемым для реализации ExtendsprotectedClass, поэтому окончательный вызов не выполняется.

РЕДАКТИРОВАНИЕ: аргументация заключается в том, что доступ protected предназначен для того, чтобы помочь подклассам реализовать те функции, которые им нужны, что дает больший доступ к внутренним элементам суперкласса, чем обычно доступно. Если бы это было доступно для всего кода, это было бы довольно близко к тому, чтобы сделать этот метод общедоступным. В принципе, подклассам доверяют не нарушать инкапсуляцию; они получают больше возможностей внутри объектов своего типа. Открытый API не должен раскрывать эти данные, но защищенный API может только для того, чтобы предоставить подклассам больше возможностей.

Ответ 2

Он работает в первом случае, потому что он вызывается из того же класса, даже к способу обращается через ссылку. Вы даже можете вызвать метод private ExtendsprotectedClass через ссылку в том же основном методе.

Ответ 3

Я считаю, что вы ответили на свой вопрос; UsesExtendedClass не наследуется от ProtectedClass, а по определению - защищенные члены доступны только в классе, в котором они объявлены/определены или в классе, который наследует тот, в котором они объявлены или определены.