[英]Class.getDeclaredMethods() of reflection peculiar behavior
I have a abstract class A, class B is concrete class that extends A.我有一个抽象类 A,类 B 是扩展 A 的具体类。
Calling B.class.getDeclaredMethods() returns class A's method signatures in addition to class B's but JAVA documentation says some thing different on getDeclaredMethods()
除了类 B 之外,调用 B.class.getDeclaredMethods() 还返回类 A 的方法签名,但 JAVA 文档在
getDeclaredMethods()
上说有些不同
"This includes public, protected, default (package) access, and private methods, but excludes inherited methods."
“这包括公共、受保护、默认(包)访问和私有方法,但不包括继承的方法。”
So from above docs i was expecting method foo() which is inherited from abstract parent class should not be returned from getDeclaredMethods()
call, but i am getting method foo() which is inherited from abstract parent class is returned from getDeclaredMethods()
call.所以从上面的文档我期待从抽象父类继承的方法 foo() 不应该从
getDeclaredMethods()
调用返回,但我得到从抽象父类继承的方法 foo() 是从getDeclaredMethods()
调用返回的.
import java.lang.reflect.*;
public class B extends A {
public static void main(String[] args) throws Exception {
Method[] methods = B.class.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
}
}
abstract class A {
public void foo() {
}
}
Can some one explain me this 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.如果您将类
A
的访问修饰符更改为public
(您需要将其放入其自己的文件中),则B.class.getDeclaredMethods()
的额外方法将消失。
(Also note that the abstract
modified on class A
is a red herring: the same thing occurs when class A
is not abstract) (另请注意,在
A
类上修改的abstract
是红鲱鱼:当A
类不是抽象时,也会发生同样的事情)
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
.这是Java 编译器中针对反射错误的解决方法:尽管
foo
是一个公共方法,但它是在包作用域类A
定义A
。 You could reflect on class B
, find the method, try to invoke it using reflection, only to get an IllegalAccessException
.您可以反思
B
类,找到该方法,尝试使用反射来调用它,结果却得到了IllegalAccessException
。
The compiler will generate a bridge method in class B
so that you can correctly reflectively invoke method foo
.编译器将在类
B
生成桥接方法,以便您可以正确地反射调用方法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)如果您将
A
的方法foo
设为final
方法,则最好证明这一点,这使得无法修复此反射错误(无法覆盖该方法)
Classes A
and B
are in package abc
and class C
is in package def
. A
类和B
类在abc
包中, C
类在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
.类
C
尝试反射性地调用公共类B
上的方法foo
,但它失败了,因为它是在非公共类A
定义A
。
Exception in thread "main" java.lang.IllegalAccessException: Class def.C can not access a member of class abc.A with modifiers "public final"
线程“main”中的异常java.lang.IllegalAccessException:类def.C无法访问带有修饰符“public final”的类abc.A的成员
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
.只需从方法
foo
删除final
关键字即可解决问题,因为编译器随后在类B
插入了合成桥接方法。
It's explained in this bug report:在此错误报告中对此进行了解释:
http://bugs.java.com/view_bug.do?bug_id=6342411 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
.但是,它始终将这些字段标记为
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.合成代码的其他示例包括:自动添加的默认构造函数、对字段中外部类的引用(如果您有非静态内部类)。
The oddity isn't in getDeclaredMethods()
- it's in the class file for B
, with a body that just calls super.foo()
.奇怪的地方不在
getDeclaredMethods()
- 它在B
的类文件中,其主体仅调用super.foo()
。
I don't fully understand it, but it appears to be related to foo()
being a public method declared in a package-private superclass.我不完全理解它,但它似乎与
foo()
是在包私有超类中声明的公共方法有关。
Some test cases:一些测试用例:
A
package-private, foo
public (as per question): method is generated in B
A
package-private, foo
public (as per question): 方法在B
生成A
package-private, foo
package-private: method isn't generated in B
A
package-private, foo
package-private: 方法不在B
生成A
public, foo
public: method isn't generated in B
A
public, foo
public: 方法不在B
生成A
public, foo
package-private: method isn't generated in B
A
public, foo
package-private: 方法不在B
生成I suspect the idea is that a third class in a different package can't "see" A
, but A.foo()
is still public
, so it should (?) be accessible through B
.我怀疑这个想法是不同包中的第三个类不能“看到”
A
,但A.foo()
仍然是public
,所以它应该 (?) 可以通过B
访问。 In order to make it accessible, B
needs to "redeclare" it.为了使其可访问,
B
需要“重新声明”它。
It's not clear to me that this is actually correct - the (?) above.我不清楚这实际上是正确的 - 上面的(?)。 JLS 6.6.1 states (emphasis mine):
JLS 6.6.1 声明(强调我的):
A member (class, interface, field, or method) of a reference type, or a constructor of a class type, is accessible only if the type is accessible and the member or constructor is declared to permit access
引用类型的成员(类、接口、字段或方法)或类类型的构造函数,只有在该类型可访问且成员或构造函数声明为允许访问时才可访问
But this code is allowed in a different package:但是这个代码是允许在不同的包:
B b = new B();
b.foo();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.