【问题标题】:Problem in the GetDeclaredMethods (java)GetDeclaredMethods (java) 中的问题
【发布时间】:2010-12-30 00:01:33
【问题描述】:

我的代码有一个小问题

我有两节课

public class A {

     public A foo(int a) {return new A();}
}

public class B extends A{

     public B foo(int x){ return new B();}
}

现在在我的代码中,我只想打印在 B 类中声明的方法

这样

B b = new B();

Method[] m = b.getClass().getDeclaredMethods();

for (int i = 0; i < m.length; i++) {

System.out.print(m[i].getName());   
}

为什么输出是

foo

foo

为什么 GetDeclaredMethods 也会在 A 类中找到 foo? 我该如何解决?

谢谢

【问题讨论】:

    标签: java reflection inheritance


    【解决方案1】:

    您可以调用 m.getDeclaringClass() 来查看它是来自 A 类还是 B 类的方法。

    【讨论】:

    • 在这两种情况下,getDeclaringClass 都返回 B
    【解决方案2】:

    默认情况下,getDeclaredMethods() 返回给定类的所有方法,以及它的父类和接口。但是,Method 对象允许您通过在 Method 上调用 getDeclaringClass() 来测试 Method 属于哪个类。因此,当您循环遍历所有 Method 对象时,您可以添加逻辑以仅打印属于 B 类的方法。

    Method[] m = b.getClass().getDeclaredMethods();
    for (int i = 0; i < m.length; i++) {
      if (m[i].getDeclaringClass().equals(B.class)) {
        System.out.print(m[i].getName());
      }
    }
    

    编辑: 上面的代码不能正常工作——它返回B 作为所有方法的声明类。 isSynthetic() 方法似乎可以按预期工作,对于被覆盖的方法(来自 A 的方法)返回 true,但对于来自 B 的方法返回 false。所以下面的代码可能就是你要找的。​​p>

    Method[] m = b.getClass().getDeclaredMethods();
    for (int i = 0; i < m.length; i++) {
      if (!m[i].isSynthetic()) {
        System.out.print(m[i]);
      }
    }
    

    【讨论】:

    • 对不起,但这不是正确的解决方案我仍然在输出中得到 2 foo
    • 我找到了方法 isSynthetic() 它为 B 类中的原始 foo 返回 false 并为 A 类中的 foo 返回 true 但因为我不熟悉 isSynthetic() 方法我不确定这是正确的使用它。这是使用此方法的正确位置吗?
    • 你是对的——isSynthetic() 确实有效。我不完全确定为什么,从我读过的合成手段在运行时动态生成,而不是由编译器生成。也许编译器会因为超类而将public A foo(int) 动态添加到B 类?不确定...会进一步调查。
    • getDeclaredMethods 不返回父类或接口的方法,还是 javadoc 错误?
    • 根据您运行的 Java 1.6 版本,您可能会得到 getDeclaredMethods() 返回的继承方法,请参阅 bugs.sun.com/bugdatabase/view_bug.do?bug_id=6815786
    【解决方案3】:

    因为 B.foo 和 A.foo 是不同的方法。如果要覆盖方法 A.foo,则方法 B.foo 必须返回类 A。

    【讨论】:

    • 正确答案,所以+1,但如果您还没有理解答案,则表示不太好。
    【解决方案4】:

    这可能有效:

    A a = new A();
    B b = new B();
    
    List<Method> aMethods = Arrays.asList(a.getClass().getMethods());
    List<Method> bMethods = Arrays.asList(b.getClass().getMethods());
    
    for ( Method m : bMethods )
    {
      if( ! aMethods.contains(m) )
      {
      //Your action code here
      }
    }
    

    【讨论】:

      【解决方案5】:

      您遇到问题的原因是您的两种方法的协变返回类型。因为您有一个协变返回类型(B 的返回类型是 B,而不是 A,与超类不同),所以 Java 在底层生成一个具有原始返回类型的单独方法,作为 1.5 之前的字节码规范与新的 Java 1.5 语言行为。

      您应该用来检查的方法是isBridge() 方法,因为它准确地表达了您打算排除的内容。所以最终的代码看起来像这样:

      Method[] methods = B.class.getDeclaredMethods();
      
      for (Method method : methods) {
      
         if (!method.isBridge()) {
             System.out.println(method.getName());
         }   
      }
      

      【讨论】:

      • 这似乎是我的问题的解决方案,谢谢 但是 IsBridge() 和 isSynthetic() 有什么区别?
      • @Dazel,基本上是 isBridge 识别 Java 编译器所做的事情,以适应泛型引入的 Java 语言特性到字节码限制中。 isSynthetic 告诉你方法是否是综合生成的,有哪些桥接方法,但更多的东西也是如此,比如内部类的一些元素。所以桥总是合成的,但合成的并不总是桥。进一步考虑,合成可能是您最关心的 - 源代码中不存在该方法,您不在乎为什么。
      • 很好,我从来没有意识到这一点。似乎他们必须做的一些事情才能让 Java 与 1.5 及更高版本的规范一起工作......很有趣。
      • 感谢您提供的知识,我永远不会自己找到这个。我试图在沙盒脚本语言中公开 Java 方法,这种多方法差异让我发疯。
      【解决方案6】:

      当你说 if(!aMethods.contains(m)) 确实包含按名称比较时?参数类型?返回值类型?因为从想要的方法到不想要的方法的唯一区别是协方差返回类型......

      【讨论】:

      • 另一个区别:一个是桥接方法(也是合成的),但这不是equals检查的)。 contains 使用 equals 方法,来自 Method.equals 的文档说:“...如果对象相同,则返回 true。如果两个方法由同一个类声明并具有相同的名称,则它们是相同的以及形式参数类型和返回类型。”
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-04
      • 2011-06-28
      • 2015-01-16
      • 2011-03-08
      • 2016-10-19
      • 2011-09-04
      相关资源
      最近更新 更多