处理继承时出现IllegalAccessError - 为什么?

Jok*_*ker 15 java inheritance

我发现了这个JDK错误,想要了解它为什么会发生.

场景(取自错误报告)非常简单:class声明一个private方法,并interface声明一个public具有相同签名的方法.它编译没有错误.

但是,当我运行此代码时,我得到了 IllegalAccessError

interface I {
    public void m();
}

class A {
    private void m() {
        System.out.println("Inside Class A");
    }

}

abstract class B extends A implements I {
}

class C extends B {
    public void m() {
        System.out.println("Inside Class C");
    }
}

public class Test {
    public static void main(String... args) {
        B b = new C();
        b.m();
    }
}
Run Code Online (Sandbox Code Playgroud)

请帮助我理解为什么这个错误存在,因为我的代码编译正常.

Exception in thread "main" java.lang.IllegalAccessError:  
tried to access method A.m()V from class Test
    at Test.main(Test.java:25)
Run Code Online (Sandbox Code Playgroud)

Joo*_*gen 7

它汇编成一切似乎都很好.

但是,它b.m()被翻译为搜索签名m(),B显然A是在接口中的第一个和(预期)之后.在A一个私人m()被发现和爆炸.

语言行为不一致,理论上可以被编译器避免.


改写

在编译期间,找到公共接口方法 - 很好.在运行期间,(无修饰符)签名在A中找到,其中方法是私有的,永远不会到达方法为公共的接口中的签名.


[FYI]使用javap进行反汇编

invokevirtual method .../.../B.m:()V
Run Code Online (Sandbox Code Playgroud)

当然是C对象.


mar*_*art 1

它可以编译,因为类 B 是一个抽象类,它声明它实现接口 I - 它假设实现将具有所需的方法。

对象 b 的类型在编译时声明为 B。如果你像下面的例子那样玩一下,你会发现它是 B 而不是 C:

为了简单起见,如果你在类 c 中有一个新方法

class C extends B {
    public void m() {
        System.out.println("C.m");
    }

    public void testFromC() {}
}
Run Code Online (Sandbox Code Playgroud)

然后尝试在你的 main 中调用它,将无法编译。

public static void main(String[] args) {
    B b = new C();
    b.testFromC(); // doesnt compile
}
Run Code Online (Sandbox Code Playgroud)

而如果你在B中添加一个方法,那就没问题了。

abstract class B extends A implements I {
    public void testFromB() { }
}

public static void main(String[] args) {
    B b = new C();
    b.testFromB(); // compiles
}
Run Code Online (Sandbox Code Playgroud)

当它运行程序时,将其视为类 B 的对象,它会从 A 中找到私有的实现。如果你通过强制转换强制 b 被视为类型 C,它将起作用。

public static void main(String[] args) {
    B b = new C();
    ((C)b).testFromC();
}
Run Code Online (Sandbox Code Playgroud)

另外,如果您删除 A for m 的私有实现,它也可以在没有强制转换的情况下工作。

class A {
    //private void m() {
    //System.out.println("A.m");
    //}
}
Run Code Online (Sandbox Code Playgroud)

这现在有效:

public static void main(String[] args) {
    B b = new C();
    b.m();
}
Run Code Online (Sandbox Code Playgroud)

因此,据我现在的理解,它看起来像在运行时首先检查 B 或其父级上的方法 m,如果没有找到任何内容,则转到 C 类的 B 的实现。