public interface A { //... } public interface B { //... } public class C implements A { //... }
foo
, overloaded for A
and B
This method is called from an instance of class C
: public class CompatibilityChecker { public String foo(A a) { return "A"; } public String foo(B b) { return "B"; } public static void main(String[] args) { CompatibilityChecker checker = new CompatibilityChecker(); System.out.println(checker.foo(new C())); } }
C implements A, B
, you get a compilation error ( for those who are not obvious to the latter, I can recommend reading about how the methods are chosen. For example, in the standard in section 15.12.2 or more just describing the ground ).C.java
, and then run the CompatibilityChecker
from the existing class file, is already a more complicated issue. Interested? I ask under the cat! public static void main(java.lang.String[]); Code: 0: new #4; //class CompatibilityChecker 3: dup 4: invokespecial #5; //Method "<init>":()V 7: astore_1 8: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 11: aload_1 12: new #7; //class C 15: dup 16: invokespecial #8; //Method C."<init>":()V 19: invokevirtual #9; //Method foo:(LA;)Ljava/lang/String; 22: invokevirtual #10; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 25: return
C
has changed, and throw an exception. Fortunately, they are mistaken, because the verifier checks only the correctness of the structure of classes and interfaces, and not the correspondence of the class-file versions.NoSuchMethodError
with NoSuchMethodError
. This assumption is also erroneous, since the foo(A)
method is called, and in a virtual table it is such one. It’s another thing if there were heirs who redefine it ... public class A { public String foo() { return "A"; } } public class B extends A { @Override public String foo() { return "B"; } } public class C extends A { @Override public String foo() { return super.foo() + "C"; } }
foo
in different ways: public class CompatibilityChecker { public static void main(String[] args) { A a = new A(); A ab = new B(); B bb = new B(); A ac = new C(); C cc = new C(); System.out.println(a.foo()); System.out.println(ab.foo()); System.out.println(bb.foo()); System.out.println(ac.foo()); System.out.println(cc.foo()); } }
A B B AC AC
A
with the result of compiling the following code: public class A { public String foo(Object dummy) { return "A"; } }
foo
is called on an instance of class A
, will definitely crash with NoSuchMethodError
. Among these attempts is also the call super.foo()
in class C
Secondly, as we have seen before, the B.foo()
method B.foo()
called successfully.A.foo
make A.foo
again the way it was, but now let's change B
and C
, completely removing the foo
method override from them: public class B extends A {} public class C extends A {}
A.foo
, and therefore will cause it in all cases, with the result that we will see only the letters “A” in the console and the complete absence of any exceptions.B
and C
again. After launch, as we can expect, dynamic dispatch will find all the entries in the virtual table, and will give exactly the same output as the one we would have received, recompiling everything. public class A { int answer; } public class B extends A {}
B
consumer: public class CompatibilityChecker { public static void main(String[] args) { B b = new B(); b.answer = 42; } }
B
our own field with the same name: public class B extends A { String answer; }
CompatibilityChecker
: public static void main(java.lang.String[]); Code: 0: new #2; //class B 3: dup 4: invokespecial #3; //Method B."<init>":()V 7: astore_1 8: aload_1 9: bipush 42 11: putfield #4; //Field B.answer:I 14: return
B
we will encounter an error. However, it turns out that this is not the case at all. Since physically only the base class has a field, the operand of the putfield
indicates exactly that field, with the result that the code continues to work after the changes. public interface A {} public class B implements A {} public class CompatibilityChecker { public static void main(String[] args) { A b = new B(); } }
foo
method to interface A
and change the implementation of the main
method of the main
class: public interface A { void foo(); } public class CompatibilityChecker { public static void main(String[] args) { A b = new B(); b.foo(); } }
AbstractMethodError: B.foo()V
, which in theory should not be. This problem is known and underlies the processing of Java bytecode. There were proposals to remedy the situation, but they still have not led to anything.NoSuchMethodError
, NoClassDefFoundError
or IncompatibleClassChangeError
will be NoClassDefFoundError
.Source: https://habr.com/ru/post/133907/
All Articles