受保护的构造函数和可访问性

2022-09-01 05:23:49

如果一个类的子类位于不同的包中,为什么我们不能用受保护的构造函数实例化该类?如果可以访问受保护的变量和方法,为什么相同的规则不适用于受保护的构造函数?

包1:

package pack1;

public class A {
    private int a;
    protected int b;
    public int c;

    protected A() {    
        a = 10;
        b = 20;
        c = 30;
    }
}

包装2:

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

设为声明构造函数的类,并设为声明中使用构造函数的最内层类。然后:CprotectedSprotected

  • 如果访问是通过超类构造函数调用,或限定的超类构造函数调用 ,其中 是表达式,则允许访问。super(...)E.super(...)E

  • 如果访问是通过匿名类实例创建表达式 或限定的匿名类实例创建表达式 ,其中 是表达式,则允许访问。new C(...){...}E.new C(...){...}E

  • 如果访问是通过简单的类实例创建表达式 new C(...)或限定类实例创建表达式 E.new C(...)(其中 E表达式)或方法引用表达式 C :: new(其中 CClassType)进行的,则不允许访问。 构造函数可由类实例创建表达式(不声明匿名类)或方法引用表达式仅从定义它的包中访问。protected

在您的情况下,通过调用 对 from 的受保护构造函数的访问是合法的。但是,访问使用是不合法的。ABBsuper()new


答案 2

JLS 6.6.7 回答了您的问题。子类仅访问其父类的受保护成员(如果它涉及其父类的实现)。因此,如果父构造函数受到保护并且它位于不同的包中,则无法在子类中实例化父对象...

6.6.7 示例:受保护的字段、方法和构造函数 考虑此示例,其中 points 包声明:

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 中发生了编译时错误:它无法访问其参数 p 的受保护成员 x 和 y,因为虽然 Point3d(发生对字段 x 和 y 的引用的类)是 Point 的子类(声明 x 和 y 的类),但它不参与 Point(参数 p 的类型)的实现。方法 delta3d 可以访问其参数 q 的受保护成员,因为类 Point3d 是 Point 的子类,并且参与 Point3d 的实现。方法 delta 可以尝试将其参数强制 (§5.5, §15.16) 为 Point3d,但如果运行时 p 的类不是 Point3d,则此强制转换将失败,从而导致异常。

方法 warp 中也会出现编译时错误:它无法访问其参数 a 的受保护成员 z,因为虽然类 Point(发生对字段 z 的引用的类)参与 Point3d(参数 a 的类型)的实现,但它不是 Point3d(声明 z 的类)的子类。