Class.getDeclaredMethods() of reflection peculiar behavior
The reason you get this is because the superclass has package level access.
If you change the access modifier of class A
to public
(you'll need to put it in its own file), the extra method in B.class.getDeclaredMethods()
disappears.
(Also note that the abstract
modified on class A
is a red herring: the same thing occurs when class A
is not abstract)
This is a workaround in the Java compiler for a bug in reflection: although foo
is a public method, it was defined in package scoped class A
. You could reflect on class B
, find the method, try to invoke it using reflection, only to get an IllegalAccessException
.
The compiler will generate a bridge method in class B
so that you can correctly reflectively invoke method foo
.
This is best demonstrated if you make the method foo
in A
a final
method, which makes it impossible to fix this reflection bug (it's not possible to override the method)
Classes A
and B
are in package abc
and class C
is in package def
. Class C
tries to reflectively invoke method foo
on class B
which is public, but it fails because it was defined in non-public class A
.
Exception in thread "main" java.lang.IllegalAccessException: Class def.C can not access a member of class abc.A with modifiers "public final"
package abc;
public class B extends A {
}
class A {
public final void foo() {
}
}
package def;
import java.lang.reflect.Method;
import abc.B;
public class C {
public static void main(String[] args) throws Exception {
Method m = B.class.getMethod("foo");
m.invoke(new B());
}
}
Just removing the final
keyword from method foo
resolves the problem, because the compiler then inserts the synthetic bridge method in class B
.
It's explained in this bug report:
http://bugs.java.com/view_bug.do?bug_id=6342411
Description
The program below fails at runtime with this error:
Exception in thread "main" java.lang.IllegalAccessException: Class refl.ClientTest can not access a member of class refl.a.Base with modifiers "public" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:578) at refl.ClientTest.main(ClientTest.java:9)
========== test/refl/a/Base.java ========== 1 package refl.a; 2 3 class Base { 4 public void f() { 5 System.out.println("Hello, world!"); 6 } 7 } ========== test/refl/a/Pub.java ========== 1 package refl.a; 2 3 public class Pub extends Base {} ========== test/refl/ClientTest.java ========== 1 package refl; 2 import refl.a.*; 3 import java.lang.reflect.*; 4 5 public class ClientTest { 6 public static void main(String[] args) throws Exception { 7 Pub p = new Pub(); 8 Method m = Pub.class.getMethod("f"); 9 m.invoke(p); 10 } 11 }
EVALUATION
The proposal is to add bridge methods in these very rare cases to fix a problem in reflection with no other forseen fix or workaround. Specifically, we would generate a bridge method when a public method is inherited from a nonpublic class into a public class.
For the reasons listed by the other answers, sometimes the compiler have to add some tricky code to your class file; this can be in the form of fields, constructors or methods. However, it always mark those fields as synthetic
. That's an actual modifier it adds, and you can check if the method is synthetic with the method:
method.isSynthetic()
So whenever you get all methods, filter your list with this method to select only the ones you actually declared in the source ;)
Other examples of synthetic code are: default constructors that get automatically added, a reference to the outer class in a field if you have a non-static inner-class.