3

So i have those 2 classes running on jdk 7:

abstract class Aclass
{
  public void foo()
  {
  }
  public void bar()
  {
  }
}

And:

public class Bclass extends Aclass
{
  public void foo(Integer one)
  {
  }
  public void bar(String two)
  {
  }
}

My goal is to load Bclass, and Bclass ONLY, print out its declared methods and parameters of those declared methods. Here is the code i use:

public static void main(String[] args)
  {
    try
    {
      Class<?> clazz = Tester.class.getClassLoader().loadClass("full_path.Bclass");
      for (Method method : clazz.getDeclaredMethods())
      {
        System.out.println("Method name: " + method.getName() + " From class: " + method.getDeclaringClass().getCanonicalName() + " with declared methods:");// test
        for (Class<?> param : method.getParameterTypes())
        {
          System.out.println(param.getCanonicalName());
        }
      }
    }
    catch (ClassNotFoundException e)
    {
      e.printStackTrace();
    }
  }

Running this code it produces the following output:

Method name: foo From class: complete_path.Bclass with declared methods:
Method name: foo From class: complete_path.Bclass with declared methods:
java.lang.Integer
Method name: bar From class: complete_path.Bclass with declared methods:
Method name: bar From class: complete_path.Bclass with declared methods:
java.lang.String

But in the javadoc's of the method [getDeclaredMethods()] i see but excludes inherited methods , this seems not to be the case according to my tests, the method apparently does load inherited methods when they are overloaded. Or am i doing something wrong?

8
  • The Javadoc for getDeclaredMethiods() states that it 'excludes inherited methods'. The output you claim to have got is therefore impossible. I note that it wasn't formatted correctly for the code you posted, which is curious. Presumably you aren't running the code you think you're running. Perhaps you called getMethods() instead of getDeclaredMethods(). Commented Jan 28, 2015 at 11:17
  • hi, nope, this is the exact code i'm running, the only thing i have edited is replacing the output's full path of the class with complete_path. because it exposes too many informations. Btw have you tried it yourself ? Commented Jan 28, 2015 at 12:20
  • @JBoy, I have tried it on Java 8 and the output differs. I only get the subclass methods as defined in the getDeclaredMethods() JavaDoc. Are you sure that this is a full example? Commented Jan 28, 2015 at 12:44
  • @EJP I tried the same code and on java 1.7 this scenario occurs. Apparently on java 8 and 1.5 (reading further down someone tried this) the implementation differs. Commented Jan 28, 2015 at 13:02
  • @wassgren apparently this only happens on java 7 Commented Jan 28, 2015 at 13:12

2 Answers 2

2

My goal is to load Bclass, and Bclass ONLY ...

It is not possible.

The JVM specification (Chapter 5) explains in great detail what has to happen when a class is loaded. One of the things that must happen is that the references to direct superclasses, and interfaces are resolved. That entails loading the respective classes / interfaces.

If (for some reason) the superclasses or interfaces cannot be loaded, then loading of the child class fails.


Loading java class methods, docs not consistent with method's behavior

The unexpected behavior of getDeclaredMethods() is a different issue. It is nothing to do with class loading.

According to this Q&A - Problem in the GetDeclaredMethods (java) - you are seeing synthetic "bridge" methods that have been added to Bclass rather than methods inherited from Aclass.

Bridge methods are described in the Java Tutorial here.

You can confirm this by using javap Bclass to look at the code in Bclass.class. See those extra bridge methods in the output.

[steve@newbox tmp]$ javap Bclass
Compiled from "Bclass.java"
public class Bclass extends Aclass {
  public Bclass();
  public void foo(java.lang.Integer);
  public void bar(java.lang.String);
  public void bar();
  public void foo();
}

Further confirmation (if you need it) can be had by printing method.isBridge() for each Method object.

Now I don't understand why this code needs bridge methods ... but this is what they are.


In summary: the behavior of getDeclaredMethods that you are seeing is consistent with the javadocs. What you are seeing is a consequence of a little known (but documented!) aspect of recent Java implementations.

Sign up to request clarification or add additional context in comments.

6 Comments

why, docs.oracle.com/javase/7/docs/api/java/lang/… does not mention anything about it
Why is that relevant? Loading of superclasses happens with all classloaders. The core mechanism for loading a class is implemented deep in the JVM and it cannot be subverted ... unless you modify the JVM itself. And then you'd have something that no longer implements Java(tm).
I agree with your statement, but this should be mentioned in the javadocs, not in the jvm specs, as this will be used by java users , not by jvm's implementators. from loadClass(): Loads the class with the specified binary name , from getDeclaredMethods(): Returns an array of Method objects reflecting all the methods declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private methods, but excludes inherited methods .
btw i still cant find a section where it is explained this 'method merging' from a superclass to a subclass while loading a class, and why thsi only happens for overloaded methods
@JBoy - that is not what is happening. See my updated answer.
|
1

It's weird
I think I did nothing wrong, but I run your program and it only prints methods in Bclass. I just changed package and I put Aclass and Bclass in same file, since only Bclass is public. Output:

Method name: foo From class: test2.Bclass with declared methods:
java.lang.Integer
Method name: bar From class: test2.Bclass with declared methods:
java.lang.String
test2.Bclass
test2.Aclass

// Also added  at the end
//   System.out.println(clazz.getCanonicalName());
//   System.out.println(clazz.getSuperclass().getCanonicalName());
// to see if I typed something wrong. 

Compiled and run using: Sun JDK 1.5.0_22.

7 Comments

Hi, thx for trying, so you made Bclass an innerclass of Aclass i suppose.
Yes: Two files, Bclass.java with both public Bclass and abstract Aclass, and Tester.java just which only contains main().
But you do get my same issue when running it in separate files? the actual structure of my project requires that to be in separate files.
There is no difference running it in two files (one for main, other for Aclass and Blcass) or using three files (each class in its file). Same output, it only prints Bclass methods. What's your JRE?
I don't have jre7 installed. I uploaded three files to pastebin. First, check them out (they are ok), and someone can download and try in their own system. Aclass: pastebin.com/UpTFEQ7w, Bclass: pastebin.com/H1vfgnhy, Tester: pastebin.com/d0s9LdEA
|

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.