我找到this JDK bug并想了解它为什么会发生。
方案(取自the bug report)非常简单: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();
}
}
请帮助我理解为什么会出现此错误我的代码编译正常。
Exception in thread "main" java.lang.IllegalAccessError:
tried to access method A.m()V from class Test
at Test.main(Test.java:25)
答案 0 :(得分:7)
它汇总,因为一切似乎都很好。
然而,mswinpr2
被翻译为在Ghostscript
中搜索签名b.m()
,显然在m()
中首先搜索并且稍后在接口中(预期)。在B
中找到了私人A
, 爆炸 。
语言行为不一致,编译器在理论上可以避免。
<强> 重述 强>
在编译期间找到了公共接口方法 - 很好。在运行期间,在A中找到(无修饰符)签名,其中方法是私有的,永远不会到达方法公开的接口中的签名。
[FYI]使用javap进行反汇编
A
当然是在m()
对象上。
答案 1 :(得分:1)
它编译,因为类B是一个抽象类,声明它实现了接口I - 它假定实现将具有所需的方法。
对象b的类型在编译时声明为B.你可以看到它是B而不是C,如下所示:
如果你在c类中有一个新方法
,那么用一个例子来简化它FTP.retrbinary
然后尝试在你的主程序中调用它,不会编译。
class C extends B {
public void m() {
System.out.println("C.m");
}
public void testFromC() {}
}
如果你在B中添加一个方法,那就没关系。
public static void main(String[] args) {
B b = new C();
b.testFromC(); // doesnt compile
}
当它运行程序时,将其视为B类的对象,它从A中找到了私有的实现。如果强制b通过强制转换为C类型,它将起作用。
abstract class B extends A implements I {
public void testFromB() { }
}
public static void main(String[] args) {
B b = new C();
b.testFromB(); // compiles
}
此外,如果您删除A的私有实现,它也可以在没有强制转换的情况下工作。
public static void main(String[] args) {
B b = new C();
((C)b).testFromC();
}
现在可以使用:
class A {
//private void m() {
//System.out.println("A.m");
//}
}
正如我现在所理解的那样,它首先在运行时检查B或其父级上的方法m,如果没有找到任何内容,则转到B类的实现。
答案 2 :(得分:1)
这是一个已知问题,目前已在此处进行跟踪:
JDK-8021581 Private class methods interfere with invocations of interface methods
此票证确实包含对问题的详细分析,对兼容性问题的讨论以及提议的解决方案的风险,并且仍然是开放的。
可以在此处找到关于该主题的较早讨论:
JDK-6684387 IllegalAccessError for code passed by compiler
(shmosel中的his comment与JDK-6691741
JLS membership algorithm is too strong for JVMS method resolution相关联 - 感谢您的支持