Java:从内部类访问受保护的字段

最近,当我尝试从内部类访问由其他类装入器装入的outer的父类中声明的受保护字段时,我遇到了运行时错误的问题。简要:java.lang.IllegalAccessError

  1. 类具有受保护字段 。Parentp
  2. 类扩展 。OuterParent
  3. 类 是在 类 中定义的内部类。InnerOuter
  4. 在类内部有一个代码:.InnerOuter.this.p
  5. 所有类都在同一包中声明。

通常,它被编译并运行良好,直到和类由不同的类装入器装入。在这种情况下,当我们尝试从 访问时,我们会得到。我发现一个旧的错误报告(似乎是一个功能)描述了这种行为:ParentOuterjava.lang.IllegalAccessErrorOuter.this.pInner

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6258289

但解决方案听起来与我相矛盾:

关键是,在失败的情况下,内部类不在同一个包中(也不是)ConcreteCommand/AbstractCommand的子类。这完全违反了受保护类的 Java 规范。

这听起来是正确的。但是,如果我们在不同的包中声明和类,但使用单个类加载器加载(只需创建示例控制台应用程序而不加载任何jar),则不会收到任何错误。因此,从技术上讲,这违反了受保护类的Java规范,但由于我们使用内部类,因此它可以工作。ParentOuter

因此,对于两种“不同包”的情况,我们有不同的行为。

  1. 在不同的包中声明,由单个类装入器装入 - OK。
  2. 在单个包中声明,由不同的类装入器装入 - 不行。

有人可以清楚地解释内部类如何访问父级的字段,以及为什么它在两种情况下的工作方式不同吗?


答案 1
  • 同一类加载器似乎正在工作
  • 我答对了吗?
  • 是否有任何单元测试用例来重现您的问题?

父类

package p1;

public class Parent {
    
    protected String p = "Value from Parent";
    
    public void test() {
        System.out.println(p);
    }

}

外部类

package p1;

public class Outer extends Parent {

    class Inner {
        public void test() {
            Outer.this.p = "Value set from Inner";
            System.out.println(Outer.this.p);
        }
    }

    public void test() {
        new Inner().test();
    }
}

主类

package p1;

public class Main {

    public static void main(String[] args) {
        Parent p = new Parent();
        p.test();
        p = new Outer();
        p.test();
    }
}

输出

Value from Parent
Value set from Inner

答案 2

在不同的包中声明,由单个类装入器装入 - OK

“受保护”访问考虑了类之间的父子关系,并允许子类访问父类的“受保护”成员,即使它们位于不同的包中。所以,我认为这是预期的。

在单个包中声明,由不同的类装入器装入 - 不行

这与运行时包有关。检查一下。现在我们知道,Parent 与 Outer 和 Inner 位于不同的运行时包中,因为是通过两个不同的类装入器加载的。同时,我们还必须记住,Outer是Parent的“子级”,但Inther不是。Inner 与 Parent 没有“Is-a”关系。

将它们放在一起:由于 Parent 位于不同的运行时包中,因此 Inner 无法访问 Parent 的“受保护”成员,因为 Inner 不是 Parent 的子级。


推荐