8

I have a small problem in my code

I have 2 classes

public class A {

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

public class B extends A{

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

now in my code I want to print only the method that was declared in class B

in this way

B b = new B();

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

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

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

why the output is

foo

foo

why the GetDeclaredMethods finds also the foo in the A class? how can i fix it?

thanks

6 Answers 6

18

The reason you are having a problem is because of the covariant return types of your two methods. Because you have a covariant return type (the return type of B is B, not A, unlike the superclass), Java under the hood generates a separate method with the original return type to act as a bridge between the pre-1.5 bytecode specification the new Java 1.5 language behavior.

The method you should be using to check, though is the isBridge() method, as it expresses exactly what you intend to exclude. So the final code would look something like this:

Method[] methods = B.class.getDeclaredMethods();

for (Method method : methods) {

   if (!method.isBridge()) {
       System.out.println(method.getName());
   }   
}
Sign up to request clarification or add additional context in comments.

4 Comments

Its seems to be the solution to my problem, thank you But what the difference between IsBridge() and isSynthetic()?
@Dazel, basically isBridge identifies things which are done by the Java compiler to fit a Java language features introduced by Generics into bytecode limitations. isSynthetic tells you if the method is synthetically generated, which bridge methods are, but more things are as well, such as some elements of inner classes. So a bridge is always synthetic, but a synthetic isn't always a bridge. Thinking about it further, synthetic might be what you care about most - the method doesn't exist in the source code, you don't care why.
Nice, I never realized this. Seems that some of the things they had to do to get Java working with the 1.5-and-up spec are... interesting.
THANK YOU for this knowledge, I never would found this on my own. I'm trying to expose Java methods in a sandboxed scripting language, and this multi-method discrepancy was driving me crazy.
2

By default, getDeclaredMethods() returns all of the methods for the given class, as well as it's parent classes and interfaces. However, the Method object allows you to test which class a Method belongs to by calling getDeclaringClass() on that Method. So when you cycle through all the Method objects, you can add logic to only print a method if it belongs to the B class.

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());
  }
}

EDIT: The above code doesn't work as desired -- it returns B as the declaring class of all methods. The isSynthetic() method appears to work as desired, returning true for an overridden method (one that came from A), but false for one that came from B. So the following code might be what you're looking for.

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

6 Comments

sorry but this is not the correct solution I still Get 2 foo in the output
I found the method isSynthetic() It return false for the original foo in class B and return true for the foo from class A but because I not familiar with the isSynthetic() method I'm not sure that this the correct use of it. Is this the right place to use this method?
You're right -- isSynthetic() does work. I'm not exactly sure why though, from what I've read synthetic means dynamically generated at runtime, not by the compiler. Maybe the compiler dynamically adds public A foo(int) to class B, because of the superclass? Not sure... will look into this further.
getDeclaredMethods does NOT return the methods from parent classes or interfaces, or is the javadoc wrong?
Depending on which version of Java 1.6 you are running, you might get the inherited methods returned with getDeclaredMethods() See bugs.sun.com/bugdatabase/view_bug.do?bug_id=6815786
|
1

Because B.foo and A.foo is different methods. If you want to override method A.foo, then method B.foo must return class A.

1 Comment

Correct answer, so +1, but it is not very well expressed if you don't already understand the answer.
0

You can call m.getDeclaringClass() to see if it's the Method from Class A or Class B.

1 Comment

in both cases the getDeclaringClass returns B
0

This may work:

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
  }
}

Comments

0

When you says if( ! aMethods.contains(m) ) does contains compare by name? arguments type? return value type? because the only difference from the wanted method to the not is the covariance return type...

1 Comment

another difference: one is a bridge method (also synthetic), but this is not checked by equals). contains use the equals method, and the documentation from Method.equals says: "... Returns true if the objects are the same. Two Methods are the same if they were declared by the same class and have the same name and formal parameter types and return type."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.