Java: защищенное ограничение доступа для подкласса объекта суперкласса

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

Вот он:

package a;
public class A{
    protected int a;
}

package b;
public class B extends A{
}

package c;
public class C extends B{
    public void accessField(){
        A ancient = new A();
        ancient.a = 2;  //A - That wouldn't work.

        a = 2;   //B - That works.
    }

}

Почему статья А) не будет работать? Какова рациональность этого ограничения для старинного доступа к суперклассу в подклассе C?
Спасибо.

Ответ 1

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

Итак, когда вы создаете еще один экземпляр A из другого пакета, это не отношение наследования, и он таким образом терпит неудачу.

Как всегда, это описано в JLS, 6.6.2:

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

Ответ 2

На самом деле вам не нужны два уровня наследования, а приведенный ниже код приведет к такому же поведению:

public class B extends A{
     public void accessField() {
        A ancient = new A();
        ancient.a = 2;  //A - That wouldn't work.

        a = 2;   //B - That works.
    }
}

Причина, по которой работает a = 2, - JLS 6.2.2.1:

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

Обратите внимание, что он не говорит прямого подкласса, а только подкласса. Поэтому a = 2 работает в классе B или C.

С другой стороны, ancient.a = 2; покрывается следующей маркерной точкой в ​​том же разделе:

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

В вашем случае Q.Id есть ancient.a = > , он будет доступен, только если тип ancient был B или подкласс B. Так, например, это скомпилировалось бы:

public class B extends A{
     public void accessField() {
        C ancient = new C();
        ancient.a = 2;  //A - That wouldn't work.
     }
}

Ответ 3

Цитата из книги Язык программирования Java 3 изд. Gosling et all - стр. 81 с. 3.5

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

Ответ 4

Цитата из JLS 6.6.2

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

Когда вы говорите,

A ancient = new A();
ancient.a = 2;

вы ничего не наследуете от древнего (объект A) и, следовательно, не отвечаете за его реализацию. Сделав C extends A, вы уже унаследовали 'a' от другого объекта A, следовательно, приведенное ниже описание работает.

a = 2;

Если,

ancient.a = 2;

работает, тогда нет никакой разницы между публичным и частным спецификатором доступа.