Class.get声明方法() 的反射特殊行为描述评估

2022-09-03 00:19:08

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

调用 B.class.getDeclaredMethods() 除了 B 类之外,还会返回类 A 的方法签名,但 JAVA 文档在getDeclaredMethods()

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

因此,从上面的文档中,我期望从抽象父类继承的方法foo()不应该从调用中返回,但是我正在从抽象父类继承的方法foo()从调用中返回。getDeclaredMethods()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() {
    }
}

有人可以解释一下我的行为吗?

enter image description here


答案 1

您获得此内容的原因是因为超类具有包级别访问权限。如果将 class 的访问修饰符更改为(需要将其放在其自己的文件中),则 中的额外方法将消失。ApublicB.class.getDeclaredMethods()

(另请注意,修改后的类是一条红鲱鱼:当类不是抽象时,也会发生同样的事情)abstractAA

这是Java编译器中针对反射错误的替代方法:尽管它是一个公共方法,但它是在包作用域类中定义的。你可以反思类,找到方法,尝试使用反射调用它,只是为了得到一个.fooABIllegalAccessException

编译器将在类中生成一个桥接方法,以便您可以正确地反射调用方法。Bfoo


如果你在方法中制作方法,这最好地证明,这使得无法修复这个反射错误(不可能重写该方法)fooAfinal

类 和 在包中,类在 包 中。类尝试在公共类上反射性地调用方法,但它失败了,因为它是在非公共类中定义的。ABabcCdefCfooBA

线程 “main” java.lang.IllegalAccessException 中的异常: Class def.C 无法访问类 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());
    }
}

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


此 bug 报告中对此进行了解释:

http://bugs.java.com/view_bug.do?bug_id=6342411

描述

下面的程序在运行时失败,并出现此错误:

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  }

评估

建议在这些非常罕见的情况下添加桥接方法,以修复反射中的问题,而无需其他可预见的修复或解决方法。具体来说,当公共方法从非公共类继承到公共类时,我们将生成一个桥接方法。


答案 2

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

method.isSynthetic()

因此,每当您获得所有方法时,请使用此方法过滤列表,以仅选择您在源;)

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