Защищенный конструктор и доступность
Почему мы не можем создать экземпляр класса с защищенным конструктором, если его дочерний элемент находится в другом пакете? Если доступ к защищенным переменным и методам возможен, почему же не применяется такое же правило для защищенного конструктора?
Pack1:
package pack1;
public class A {
private int a;
protected int b;
public int c;
protected A() {
a = 10;
b = 20;
c = 30;
}
}
Pack2:
package pack2;
import pack1.A;
class B extends A {
public void test() {
A obj = new A(); // gives compilation error; why?
//System.out.println("print private not possible :" + a);
System.out.println("print protected possible :" + b);
System.out.println("print public possible :" + c);
}
}
class C {
public static void main(String args[]) {
A a = new A(); // gives compilation error; why?
B b = new B();
b.test();
}
}
Ответы
Ответ 1
В соответствии с Java Spec (https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.2.2)
6.6.2.2. Квалифицированный доступ к конструктору protected
Пусть C
- класс, в котором объявлен конструктор protected
, и пусть S
является самым внутренним классом, в объявлении которого используется конструктор protected
. Тогда:
-
Если доступ осуществляется вызовом конструктора суперкласса super(...)
или квалифицированным вызовом конструктора суперкласса E.super(...)
, где E
является первичным выражением, доступ разрешен.
-
Если доступ определяется выражением создания экземпляра анонимного класса new C(...){...}
или выражением для создания экземпляра квалифицированного анонимного класса E.new C(...){...}
, где E
является основным выражением, доступ разрешен.
-
Если доступ осуществляется с помощью простого экземпляра создания экземпляра класса new C(...)
или выражения для выражения класса квалифицированного класса E.new C(...)
, где E
является первичным выражением или ссылочным выражением метода C :: new
, где C
- тип ClassType, тогда доступ не разрешен. Конструктор protected
может быть вызван выражением создания экземпляра класса (которое не объявляет анонимный класс) или ссылочным выражением метода только изнутри пакета, в котором он определен.
В вашем случае доступ к защищенному конструктору A
из B
будет легальным из конструктора B
посредством вызова super()
. Однако доступ с использованием new
не является законным.
Ответ 2
JLS 6.6.7 отвечает на ваш вопрос. Подкласс получает доступ только к защищенным членам его родительского класса, если он предполагает реализацию его родителя. Поэтому вы не можете создавать родительский объект в дочернем классе, если родительский конструктор защищен и находится в другом пакете...
6.6.7 Пример: защищенные поля, методы и конструкторы Рассмотрим этот пример, где пакет точек заявляет:
package points;
public class Point {
protected int x, y;
void warp(threePoint.Point3d a) {
if (a.z > 0) // compile-time error: cannot access a.z
a.delta(this);
}
}
и пакет threePoint объявляет:
package threePoint;
import points.Point;
public class Point3d extends Point {
protected int z;
public void delta(Point p) {
p.x += this.x; // compile-time error: cannot access p.x
p.y += this.y; // compile-time error: cannot access p.y
}
public void delta3d(Point3d q) {
q.x += this.x;
q.y += this.y;
q.z += this.z;
}
}
который определяет класс Point3d. ошибка времени компиляции возникает в метод delta здесь: он не может получить доступ защищенные члены x и y его параметр p, потому что в то время как Point3d (класс, в котором ссылки на поля x и y встречаются) является подклассом Точка (класс, в котором x и y являются объявлен), он не участвует в реализация Точки (тип параметр p). Метод delta3d могут получить доступ к защищенным членам его параметр q, поскольку класс Point3d является подклассом Point и является участвующих в осуществлении Point3d. Метод delta может попытаться (§5.5, §15.16) его параметр быть Point3d, но это приведение fail, вызывая исключение, если класс p во время выполнения не были Point3d.
Ошибка времени компиляции также возникает в метод warp: он не может получить доступ к защищенный член z его параметра a, потому что в то время как класс Point ( класс, в котором ссылка на поле z) участвует в реализация Point3d (тип параметра а), это не подкласс Point3d (класс в который z объявлен).
Ответ 3
Зачем вам нужен A obj=new A();
в классе, тогда как объект класса b сам является объектом class A
И в классе c он дает ошибку, потому что вы получаете доступ к защищенному свойству класса A, который является конструктором.
Чтобы получить объект класса А в этом случае, вы должны использовать эту функцию в классе A
static A getInstance()
{
A obj = new A(); // create obj of type A.
return obj; // returns that object by this method. No need to use 'New' kind of instantiation.
}
Ответ 4
Я согласен с предыдущими плакатами, не знаю, почему вы хотели бы это сделать (создайте родительский экземпляр таким образом в расширении класса), но вы можете даже сделать что-то вроде этого:
public void test() {
A obj = new A(){}; // no compilation error; why? you use anonymous class 'override'
...
Ответ 5
это работает так же, как защищенные поля.