Java:子类对超类对象的受保护访问限制

13
我知道这个问题在这个论坛里已经被问过,但是我想再问一次,因为目前我还没有看到好的答案。
以下是要翻译的内容:
package a;
public class A{
    protected int a;
}

package b;
public class B extends A{
}

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

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

}

为什么A条款不起作用?子类C中对于超类对象的访问受到此限制背后的逻辑是什么?
谢谢。


1
我猜这里的一个继承步骤是多余的,只是为了说明情况。 - Vitaly
2
@HovercraftFullOfEels 我想这个问题更多是理论上的而不是实际上的,但并不无趣。 - assylias
4个回答

7

如果通过继承(即在层次结构内)访问受保护的成员,则只能在同一包之外访问。

因此,当您从不同的包中创建A的另一个实例时,那不是继承关系,因此会失败。

像往常一样,在JLS 6.6.2中涵盖了这一点:

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


我感到困惑。同一部分还说:“只有在子类S的主体内才允许访问”,它并没有说直接的子类,只是子类... - assylias
@assylias 说得好 - 在这种情况下,我所说的“直接”并不是指那个意思,但我明白这可能会产生歧义,所以我稍微改了一下措辞! - Michael Berry

5
其实你不需要两级继承,下面的代码会导致相同的行为:
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是被声明了protected成员的类。只有在C的子类S中才可以访问该成员。

需要注意的是,它并没有说“直接”子类,而是子类。所以a = 2可以在B类或者C类中执行。

另一方面,ancient.a = 2;则被同一节中下一个符号点所覆盖:

如果访问是通过限定名称Q.Id完成的,其中Q是ExpressionName,则只有当表达式Q的类型为S或S的子类时才允许访问。

在您的情况下,Q.Idancient.a => 只有当ancient的类型为BB的子类时,才能访问它。例如,以下代码可以编译:

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

1

引自《Java编程语言第三版》Gosling等人所著-第81页3.5节

"什么是Protected的真正含义" - 除了在类内部和同一包内部的代码可以访问外,受保护成员还可以通过与类相同类型或其子类之一的对象引用从类中访问。


0

引用自JLS 6.6.2

对象的受保护成员或构造函数只能由负责该对象实现的代码从其声明的包之外访问。

当你说,

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

您没有从古代(A对象)继承任何内容,因此不需要负责其实现。通过让C extends A,您已经从一个不同的A对象中继承了“a”,因此下面的语句可以正常工作。

a = 2;

如果,

ancient.a = 2;

如果程序正常工作,那么公共访问修饰符和私有访问修饰符之间没有区别。


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