Java 反射行为的Class.getDeclaredMethods()

Java 反射行为的Class.getDeclaredMethods(),java,reflection,Java,Reflection,我有一个抽象类a,类B是扩展a的具体类 调用B.class.getDeclaredMethods()除了返回类B的方法签名外,还返回类A的方法签名,但是JAVA文档在getDeclaredMethods()上说了一些不同的东西 “这包括公共、受保护、默认(包) 访问和私有方法,但不包括继承的方法。“ 因此,从上面的文档中,我希望从抽象父类继承的方法foo()不应该从getDeclaredMethods()call返回,但我得到的是从抽象父类继承的方法foo()是从getDeclaredMetho

我有一个抽象类a,类B是扩展a的具体类

调用B.class.getDeclaredMethods()除了返回类B的方法签名外,还返回类A的方法签名,但是JAVA文档在
getDeclaredMethods()上说了一些不同的东西

“这包括公共、受保护、默认(包) 访问和私有方法,但不包括继承的方法。“

因此,从上面的文档中,我希望从抽象父类继承的方法foo()不应该从
getDeclaredMethods()
call返回,但我得到的是从抽象父类继承的方法foo()是从
getDeclaredMethods()
call返回的

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() {
    }
}
import java.lang.reflect.*;
公共类B扩展了A{
公共静态void main(字符串[]args)引发异常{
方法[]methods=B.class.getDeclaredMethods();
for(int i=0;i
有人能解释我的这种行为吗


看起来您找到了一个尚未修复的问题。

奇怪之处不在
getDeclaredMethods()
-它位于
B
的类文件中,其主体仅调用
super.foo()

我不完全理解它,但它似乎与
foo()
是在包私有超类中声明的公共方法有关

一些测试用例:

  • A
    package private,
    foo
    public(根据问题):方法在
    B
  • A
    package private,
    foo
    package private:方法未在
    B
  • A
    public,
    foo
    public:方法未在
    B
  • A
    public,
    foo
    package private:方法未在
    B
我怀疑这个想法是不同包中的第三个类无法“看到”
a
,但是
a.foo()
仍然是
公共的
,因此应该可以通过
B
访问它。为了使其可访问,
B
需要“重新声明”它

我不清楚上面的(?)是否正确。JLS 6.6.1规定(重点):

引用类型的成员(类、接口、字段或方法)或类类型的构造函数只有在类型可访问且声明该成员或构造函数允许访问时才可访问

但此代码允许在不同的包中使用:

B b = new B();
b.foo();

之所以会出现这种情况,是因为超类具有包级别的访问权限。 如果将类
A
的访问修饰符更改为
public
(需要将其放入自己的文件中),则
B.class.getDeclaredMethods()中的额外方法将消失

(还请注意,在类
A
上修改的
abstract
是一种误导:当类
A
不是抽象时,也会发生同样的情况)

这是Java编译器中针对反射错误的一个变通方法:尽管
foo
是一个公共方法,但它是在包范围的类
a
中定义的。您可以对类
B
进行反思,找到该方法,尝试使用反思调用它,结果得到一个
IllegalAccessException

编译器将在类
B
中生成一个桥接方法,这样您就可以正确地反射调用方法
foo


如果您在
A
A
final
方法中使用
foo
方法,这是最好的证明,这使得无法修复此反射错误(无法覆盖该方法)

A
B
在包
abc
中,类
C
在包
def
中。类
C
尝试在类
B
上反射调用方法
foo
,该方法是公共的,但失败的原因是它是在非公共类
A
中定义的

线程“main”java.lang.IllegalAccessException:类中出现异常 def.C无法访问修改器为“public”的abc.a类成员 最后的“

只需从方法
foo
中删除
final
关键字即可解决问题,因为编译器随后会在类
B
中插入合成桥方法


此错误报告对此进行了解释:

描述 以下程序在运行时失败,并出现此错误:

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)
评价 建议在这些非常罕见的情况下添加桥接方法来修复 没有其他forseen修复或解决方法的反射问题。 具体地说,当一个公共方法 从非公共类继承到公共类


出于其他答案列出的原因,有时编译器不得不向类文件中添加一些棘手的代码;这可以是字段、构造函数或方法的形式。但是,它总是将这些字段标记为
合成
。这是它添加的实际修改器,您可以检查该方法是否与该方法合成:

method.isSynthetic()
因此,无论何时获得所有方法,都要使用此方法筛选列表,以仅选择您在源代码中实际声明的方法;)


合成代码的其他示例有:自动添加的默认构造函数,如果有非静态内部类,则在字段中引用外部类。

无法复制(仅打印main):请检查输出attachedInteresting-它意外地为我打印了
public void B.foo()
。有趣的是,
javap-cb
显示了一个
B.foo()
方法,基本上调用了
super.foo()
@assylias:我还发现了一些有趣的方面。我不这么认为。我相信它准确地反映了类文件中的内容。@JonSk
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  }
method.isSynthetic()