为什么不同包中的子类无法访问受保护的实例成员,但可以访问受保护的类成员?

7
package one;

public class A {
    protected int first;
    protected static int second;
}

package two;

import one.A;

public class B extends A {
    public void someMethod() {
        this.first = 5; //works as expected
        B.second = 6; //works
        A a = new A();
        // a.first = 7; does not compile

        //works just fine, but why?
        a.second = 8; 
        A.second = 9;
    }
}

为什么静态字段没有同样的限制,背后的想法是什么?
1个回答

6

来自JLS 6.6.2

对象的受保护成员或构造函数只能被负责该对象实现的代码访问,即使在声明它的包之外。

来自6.6.2.1

假设C是声明受保护成员的类。只有在C的子类S中才允许访问。

this.first = 5; 可以工作是因为 BA 的实现者。

A.secondB.second 都可以工作,因为这个限制只适用于对象的成员。

至于为什么要这样指定,您需要问定义规范的人——我们只能做出假设。6.6.2.1甚至有一个类似于这个问题的示例:
请考虑这个示例,其中点包声明:
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;
    }
}

在方法delta中出现编译时错误:它无法访问其参数p的受保护成员x和y,因为Point3d(包含对字段x和y的引用的类)是Point的子类(其中声明了x和y的类),但它没有参与Point(参数p的类型)的实现。方法delta3d可以访问其参数q的受保护成员,因为Point3d类是Point类的子类,且参与Point3d的实现。
我建议查看为什么我们不应在Java中使用protected staticprotected的语义针对实例成员-protected staticprotected的目的相矛盾,这可能是为什么它没有以同样的方式受到限制的原因。

嗨,我还没有得到答案,如何访问受保护的静态变量?在这种情况下,a.second = 8; 是可以访问的。 - nagendra547
@nagendra547 这个限制只适用于实例成员;类成员不受此限制。规范仅要求对实例成员进行此操作。 - Dioxin
如果B没有继承A,那么你无法访问类成员,例如a.second。 - nagendra547
@nagendra547 这个问题在6.6.2.1中有所涉及:“让C成为声明受保护成员的类。只有在子类S的主体内才允许访问。”-由于BA的子类,因此可以访问受保护的成员。 6.6.2强制执行实例成员的限制。两个不同的合同。如果BA在同一个包中,则B仍然可以访问A.second而无需扩展。 - Dioxin
我必须说,“参与实现”这个词组用得相当糟糕。“成员”会更好,也更简单。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接