Class.getDeclaredMethods()反射不需要的行为

Jok*_*ker 17 java reflection

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

除了B类之外,调用B.class.getDeclaredMethods()还会返回A类的方法签名,但JAVA文档说明了一些不同之处 getDeclaredMethods()

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

所以从上面的文档中我期待从抽象父类继承的方法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() {
    }
}
Run Code Online (Sandbox Code Playgroud)

有人可以解释我这种行为.

在此输入图像描述

Erw*_*idt 19

你得到这个的原因是因为超类具有包级访问权限.如果将类的访问修饰符更改Apublic(您需要将其放在自己的文件中),则额外的方法会B.class.getDeclaredMethods()消失.

(另请注意,abstract类上的修改A是一个红色的鲱鱼:当类A不是抽象时会发生同样的事情)

这是Java编译器中针对反射中的错误的一种解决方法:虽然它foo是一个公共方法,但它是在包作用域类中定义的A.你可以反思类B,找到方法,尝试使用反射来调用它,只是为了得到一个IllegalAccessException.

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


如果在方法fooA创建final方法,则最好地证明这一点,这使得无法修复此反射错误(无法覆盖该方法)

AB使用软件包abc和类C是包def.类C试图反射性地调用公共foo类的方法B,但它失败了,因为它是在非公共类中定义的A.

线程"main"中的异常java.lang.IllegalAccessException:类def.C无法使用修饰符"public final"访问类abc.A的成员

package abc;

public class B extends A {
}

class A {
    public final void foo() {
    }

}
Run Code Online (Sandbox Code Playgroud)
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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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


这个错误报告中解释了这个问题:

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)
Run Code Online (Sandbox Code Playgroud)
========== 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  }
Run Code Online (Sandbox Code Playgroud)

评估

该提议是在这些非常罕见的情况下添加桥接方法来解决反射中的问题,而没有其他更好的修复或解决方法.具体来说,当公共方法从非公共类继承到公共类时,我们将生成桥接方法.


Lua*_*ico 13

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

method.isSynthetic()
Run Code Online (Sandbox Code Playgroud)

因此,每当您获得所有方法时,使用此方法过滤列表以仅选择您在源中实际声明的那些;)

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