getMethod() works and works not

Discussion in 'Java' started by Alexander Burger, Nov 27, 2010.

  1. Hi all,

    can anybody explain why the getMethod() call (in the line commented with
    "???" below) throws a NoSuchMethodException?

    Perhaps I'm doing something obvious wrong, but I couldn't find any hint.

    ################################################################
    import javax.swing.*;
    import java.lang.reflect.*;

    public class F {
    public static void main(String[] args) throws Exception {
    JFrame frame = new JFrame("Title");
    JPanel panel = (JPanel)frame.getContentPane();
    JTextArea area = new JTextArea(10, 40);
    Method method;

    frame.setSize(300, 200);
    frame.setLocation(200, 200);

    // This works ('add' JTextArea to JPanel):
    panel.add(area);

    // This, however, does not work (same 'add' JTextArea to JPanel):
    // ??? method = panel.getClass().getMethod("add", area.getClass());

    // This again works:
    method = panel.getClass().getMethod("setName", "newName".getClass());

    frame.setVisible(true);
    }
    }
    ################################################################

    The direct call

    panel.add(area);

    works, but the corresponding getMethod()

    method = panel.getClass().getMethod("add", area.getClass());

    throws

    NoSuchMethodException: javax.swing.JPanel.add(javax.swing.JTextArea)

    getMethod() on another method like "setName" with a string type works.

    Both methods - add() and setName() - should be inherited from the
    superclasses of JPanel. They are 'public' in Container and/or Component.

    What is the difference, or what am I doing wrong?

    Cheers,
    - Alex
    --
    Software Lab. Alexander Burger
    Bahnhofstr. 24a, D-86462 Langweid
    , www.software-lab.de, +49 8230 5060
    Alexander Burger, Nov 27, 2010
    #1
    1. Advertising

  2. Alexander Burger

    Aeris Guest

    Alexander Burger wrote:

    > What is the difference, or what am I doing wrong?


    «add» prototype is
    public Component add(Component comp)
    So you have to do
    method = panel.getClass().getMethod("add", Component.class);

    your code searches for a
    public Component add(*JTextArea* comp)
    which doesn't exist
    Aeris, Nov 27, 2010
    #2
    1. Advertising

  3. Aeris <> wrote:
    > «add» prototype is
    > public Component add(Component comp)
    > So you have to do
    > method = panel.getClass().getMethod("add", Component.class);


    Thanks, Aeris.

    However, this was just an example. The actual code is generic, so I
    can't pass a specific class. I have to call getClass() on the object,
    right?

    And, according to the references, getMethod() searches the class and
    superclasses for a matching method. This seems not to work here.

    Cheers,
    - Alex

    --
    Software Lab. Alexander Burger
    Bahnhofstr. 24a, D-86462 Langweid
    , www.software-lab.de, +49 8230 5060
    Alexander Burger, Nov 27, 2010
    #3
  4. Alexander Burger

    Aeris Guest

    Alexander Burger wrote:

    > However, this was just an example. The actual code is generic, so I
    > can't pass a specific class. I have to call getClass() on the object,
    > right?


    No
    Even your code is generic, this is always a Component, and nothing else

    > And, according to the references, getMethod() searches the class and
    > superclasses for a matching method. This seems not to work here.


    Search for a method in hierarchy, not for a parameter.

    class U

    class V extends U

    class A
    void bar(U u)

    class B extends A


    B.getMethod("bar", U.class) returns the A.bar(U) by inheritance
    But B.getMethod("bar", V.class) and A.getMethod("bar", V.class) fail,
    because there is no B.bar(V) or A.bar(V)

    But this is not a problem because this code works as expected:
    B.getMethod("bar", U.class).invoke(new V())
    and call the A.bar(U) with a V parameter, because V is an U by inheritance
    Aeris, Nov 27, 2010
    #4
  5. Alexander Burger

    markspace Guest

    On 11/27/2010 8:32 AM, Alexander Burger wrote:
    > Aeris<> wrote:
    >> «add» prototype is
    >> public Component add(Component comp)
    >> So you have to do
    >> method = panel.getClass().getMethod("add", Component.class);

    >
    > Thanks, Aeris.
    >
    > However, this was just an example. The actual code is generic, so I
    > can't pass a specific class. I have to call getClass() on the object,
    > right?
    >
    > And, according to the references, getMethod() searches the class and
    > superclasses for a matching method. This seems not to work here.



    This could still be made to work, even for the generic case. Can you
    tell us more about the context? There's a short cut if you are using
    the Proxy class, otherwise you'll have to go about it the longer way.
    markspace, Nov 27, 2010
    #5
  6. "markspace" <> wrote in message
    news:icrg07$b2k$-september.org...
    > On 11/27/2010 8:32 AM, Alexander Burger wrote:
    >> Aeris<> wrote:
    >>> «add» prototype is
    >>> public Component add(Component comp)
    >>> So you have to do
    >>> method = panel.getClass().getMethod("add", Component.class);

    >>
    >> Thanks, Aeris.
    >>
    >> However, this was just an example. The actual code is generic, so I
    >> can't pass a specific class. I have to call getClass() on the object,
    >> right?
    >>
    >> And, according to the references, getMethod() searches the class and
    >> superclasses for a matching method. This seems not to work here.

    >
    >
    > This could still be made to work, even for the generic case. Can you tell
    > us more about the context? There's a short cut if you are using the Proxy
    > class, otherwise you'll have to go about it the longer way.
    >


    To begin with, why is the OP using reflection at all? It's of no value in
    the code actually posted, which could simply call method the normal way.
    It's fairly uncommon for reflection to be needed, and it may be that the
    best solution to the problem (whatever that turns out to be) won't require
    it.
    Mike Schilling, Nov 27, 2010
    #6
  7. Mike Schilling <> wrote:
    > To begin with, why is the OP using reflection at all? It's of no value in
    > the code actually posted, which could simply call method the normal way.


    Sure, but this is just a prepared, reproducible example.

    The actual code is the Java version of PicoLisp. The plain runtime
    system ist at "http://software-lab.de/ersatz.tgz", the full system
    including sources at "http://software-lab.de/picoLisp.tgz".

    There is a generic 'java' function which allows to call arbitrary
    constructors and methods, on arbitrary objects. And, this indeed works
    most of the cases, but sometimes (like here for 'JPanel' and 'add') not.

    The lisp-level function 'java' accepts four syntax patterns:

    (java 'obj ['cnt]) -> any
    Converts a Java object back to Lisp data

    (java 'obj 'msg 'any ..) -> obj
    Invokes a method 'msg' on an object 'obj' with arguments 'any'

    (java 'cls 'msg 'any ..) -> obj
    Invokes a static method 'msg' in class 'cls' with arguments 'any'

    (java 'cls 'T 'any ..) -> obj
    Returns a new object of class 'cls' by calling the constructor of
    that class with arguments 'any'.

    For example, this works (analog to the posted plain Java example):

    (setq
    frame (java "javax.swing.JFrame" T "Title")
    panel (java frame "getContentPane")
    area (java "javax.swing.JTextArea" T 10 40) )

    (java frame "setSize" 300 200)
    (java frame "setLocation" 100 100)

    # ??? (java panel "add" JTextArea)

    (java frame "setVisible" T)

    Just the line with "???" does not, and this is the case which also has
    "???" in the original post.

    Cheers,
    - Alex
    Alexander Burger, Nov 27, 2010
    #7
  8. Hi markspace,

    > This could still be made to work, even for the generic case. Can you
    > tell us more about the context? There's a short cut if you are using
    > the Proxy class, otherwise you'll have to go about it the longer way.


    How would that longer way look like?

    About the context I just wrote in another reply, so I don't repeat it
    here.

    There must be a way, because the combination of Java compiler and
    runtime system does exactly the same.

    Cheers,
    - Alex
    Alexander Burger, Nov 27, 2010
    #8
  9. Hi Aeris,


    >
    > class U
    >
    > class V extends U
    >
    > class A
    > void bar(U u)
    >
    > class B extends A
    >
    >
    > B.getMethod("bar", U.class) returns the A.bar(U) by inheritance
    > But B.getMethod("bar", V.class) and A.getMethod("bar", V.class) fail,
    > because there is no B.bar(V) or A.bar(V)


    I understand. But in my case I have no class at all. Just an object and
    a method name.

    From the reference I expect that getMethod() is able by searching up the
    class hierarchy to locate a method matching the signature (i.e. the
    array of classes passed in the second argument to getMethod()). Otherwise
    it would be pretty useless.

    Cheers,
    - Alex
    Alexander Burger, Nov 27, 2010
    #9
  10. Alexander Burger <> wrote:
    > For example, this works (analog to the posted plain Java example):
    >
    > (setq
    > frame (java "javax.swing.JFrame" T "Title")
    > panel (java frame "getContentPane")
    > area (java "javax.swing.JTextArea" T 10 40) )
    >
    > (java frame "setSize" 300 200)


    This for example is an interesting case.

    As I said, it works, and in effect it calls

    getMethod("setSize", java.lang.JFrame)

    But the class 'JFrame' doesn't have 'setSize' defined, it inherits it
    from java.awt.Window. Why does it work here, but not in the following
    case?

    > # ??? (java panel "add" JTextArea)


    Cheers,
    - Alex
    Alexander Burger, Nov 27, 2010
    #10
  11. Alexander Burger <> wrote:
    >> (java frame "setSize" 300 200)

    >
    > This for example is an interesting case.
    >
    > As I said, it works, and in effect it calls
    >
    > getMethod("setSize", java.lang.JFrame)


    Oops, it is

    frame.getClass().getMethod("setSize", Integer.TYPE, Integer.TYPE)

    to be exact.
    Alexander Burger, Nov 27, 2010
    #11
  12. "Alexander Burger" <> wrote in message
    news:icripp$b4k$...
    > Hi Aeris,
    >
    >
    >>
    >> class U
    >>
    >> class V extends U
    >>
    >> class A
    >> void bar(U u)
    >>
    >> class B extends A
    >>
    >>
    >> B.getMethod("bar", U.class) returns the A.bar(U) by inheritance
    >> But B.getMethod("bar", V.class) and A.getMethod("bar", V.class) fail,
    >> because there is no B.bar(V) or A.bar(V)

    >
    > I understand. But in my case I have no class at all. Just an object and
    > a method name.
    >
    > From the reference I expect that getMethod() is able by searching up the
    > class hierarchy to locate a method matching the signature (i.e. the
    > array of classes passed in the second argument to getMethod()). Otherwise
    > it would be pretty useless.


    The precise search used by the compiler to find a matching method is
    documentedn the JLS at
    http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.
    As you'll see it's*very* complicated, and getMethod() doesn't even try to
    replicate it. Rather, getMethod() assumes you've given it the genuine
    parameter types (not subclasses thereof), and looks up the hierarchy for an
    exact match.

    You could probably get what you want with a small subset of the logic in
    15.12, e.g.:

    0. Given the object O you want to call the method on, the method name M,
    and a list of arguments A:
    1. Call O.getClass().getMethods() to find all the public methods callable on
    O.
    2. Reject the ones not named M
    3. Reject the ones with the wrong number of arguments. (Be careful here if
    you're going to support variable-argument methods)
    4. If more than one remains, check which have compatible argument types
    5. If exactly one remains, call it. Otherwise throw an exception.
    Mike Schilling, Nov 27, 2010
    #12
  13. Alexander Burger

    markspace Guest

    On 11/27/2010 10:24 AM, Alexander Burger wrote:

    > Mike Schilling<> wrote:
    >> To begin with, why is the OP using reflection at all? It's of no value in
    >> the code actually posted, which could simply call method the normal way.


    > Sure, but this is just a prepared, reproducible example.
    >
    > The actual code is the Java version of PicoLisp. The plain runtime
    > system ist at "http://software-lab.de/ersatz.tgz", the full system
    > including sources at "http://software-lab.de/picoLisp.tgz".



    I tend to agree with Mike, nevertheless. This kind of reflection
    binding is something only an author could love. Static types and strong
    typing are too valuable to give up.

    The only way to do this is to search the method signatures. Probably
    something you could have done, but I promised help, so here it is. Note
    this is a first fit algorithm, not a best fit. If there are multiple
    matches for signatures (i.e., the method name is overloaded) we take the
    first one that works.


    package test;

    import javax.swing.*;
    import java.lang.reflect.*;

    public class F
    {

    public static void main( String[] args )
    throws Exception
    {
    JFrame frame = new JFrame( "Title" );
    JPanel panel = (JPanel) frame.getContentPane();
    JTextArea area = new JTextArea( 10, 40 );

    frame.setSize( 300, 200 );
    frame.setLocation( 200, 200 );

    // This works ('add' JTextArea to JPanel):
    panel.add( area );

    // This, however, does not work (same 'add' JTextArea to JPanel):
    //??? method = panel.getClass().getMethod("add",area.getClass());
    exeMethod( panel, "add", area );

    frame.setVisible( true );
    }

    public static Object exeMethod( Object o, String method,
    Object... params )
    throws NoSuchMethodException, IllegalAccessException,
    IllegalArgumentException, InvocationTargetException
    {
    Object retVal = null;
    Method[] methodList;
    methodList = o.getClass().getMethods();
    looking: // looking for methods
    for( Method m : methodList ) {
    if( m.getName().equals( method ) ) {
    Class<?>[] paramTypes = m.getParameterTypes();
    if( paramTypes.length == params.length ) {
    for( int i = 0; i < paramTypes.length; i++ ) {
    Class<?> class1 = paramTypes;
    if( !(class1.isInstance( params )) ) {
    continue looking;
    }
    }
    // all parameters check out here:
    System.err.println( "Calling: " + m );
    retVal = m.invoke( o, params );
    }
    }
    }
    return retVal;
    }
    }
    markspace, Nov 27, 2010
    #13
  14. Alexander Burger

    markspace Guest

    On 11/27/2010 11:12 AM, Mike Schilling wrote:

    > 5. If exactly one remains, call it. Otherwise throw an exception.



    This is a good idea and something I might want to think about
    implementing in my example. Hmm....
    markspace, Nov 27, 2010
    #14
  15. Alexander Burger

    markspace Guest

    > On 11/27/2010 11:12 AM, Mike Schilling wrote:
    >
    >> 5. If exactly one remains, call it. Otherwise throw an exception.


    Done.

    package test;

    import javax.swing.*;
    import java.lang.reflect.*;

    public class F
    {

    public static void main( String[] args )
    throws Exception
    {
    JFrame frame = new JFrame( "Title" );
    JPanel panel = (JPanel) frame.getContentPane();
    JTextArea area = new JTextArea( 10, 40 );

    frame.setSize( 300, 200 );
    frame.setLocation( 200, 200 );

    // This works ('add' JTextArea to JPanel):
    panel.add( area );

    // This, however, does not work (same 'add' JTextArea to JPanel):
    // ??? method = panel.getClass().getMethod("add",area.getClass());
    exeMethod( panel, "add", area );

    frame.setVisible( true );
    }

    public static Object exeMethod( Object o, String method, Object...
    params )
    throws NoSuchMethodException, IllegalAccessException,
    IllegalArgumentException, InvocationTargetException
    {
    Method[] methodList;
    methodList = o.getClass().getMethods();
    Method invokeMethod = null;
    looking: // looking for methods
    for( Method m : methodList ) {
    if( m.getName().equals( method ) ) {
    Class<?>[] paramTypes = m.getParameterTypes();
    if( paramTypes.length == params.length ) {
    for( int i = 0; i < paramTypes.length; i++ ) {
    Class<?> class1 = paramTypes;
    if( !(class1.isInstance( params )) ) {
    continue looking;
    }
    }
    // all parameters check out:
    System.err.println( "Canidate: " + m );
    if( invokeMethod == null ) {
    invokeMethod = m;
    } else {
    // more than one canidate, bail
    throw new IllegalArgumentException( method+
    " has multiple canidates, cannot resolve.");
    }
    }
    }
    }
    return invokeMethod.invoke( o, params );
    }
    }
    markspace, Nov 27, 2010
    #15
  16. Alexander Burger

    Tom Anderson Guest

    On Sat, 27 Nov 2010, Alexander Burger wrote:

    > Hi markspace,
    >
    >> This could still be made to work, even for the generic case. Can you
    >> tell us more about the context? There's a short cut if you are using
    >> the Proxy class, otherwise you'll have to go about it the longer way.

    >
    > How would that longer way look like?
    >
    > About the context I just wrote in another reply, so I don't repeat it
    > here.
    >
    > There must be a way, because the combination of Java compiler and
    > runtime system does exactly the same.


    One option would be to sidestep the problem of reproducing what the
    compiler does, and just use the compiler. Could you generate little source
    fragments at runtime with the relevant inputs, then compile them to
    bytecode on the fly? If you had an interface like:

    public interface Invoker {
    public Object invoke(Object receiver, Object... params);
    }

    Then when you're in the situation that the receiver is a
    javax.swing.JPanel (i'm guessing here), the method is called "add", and
    there is one parameter, a javax.swing.JTextArea, you would generate code
    like this (from a very simple template - note that all the types are based
    on the runtime class of the parameters you happen to have at the time,
    which is easily determined):

    class javax$swing$JPanel_add_javax$swing$JTextArea implements Invoker {
    public Object invoke(Object receiver, Object... params) {
    javax.swing.JPanel castReceiver = (javax.swing.JPanel)receiver;
    if (params.length != 1) throw new IllegalArgumentException(); // or something more appropriate
    javax.swing.JTextArea param0 = (javax.swing.JTextArea)params[0];
    // you also need to do something about exceptions
    return castReceiver.add(param0);
    }
    }

    Compile it, put the generated class file on your classpath, then load it
    and use it. You would keep a global cache of generated invokers, and reuse
    them rather than generating them afresh where you could.

    tom

    --
    YOUR MIND IS A NIGHTMARE THAT HAS BEEN EATING YOU: NOW EAT YOUR MIND. --
    Kathy Acker, Empire of the Senseless
    Tom Anderson, Nov 27, 2010
    #16
  17. "Tom Anderson" <> wrote in message
    news:...
    > On Sat, 27 Nov 2010, Alexander Burger wrote:
    >
    >> Hi markspace,
    >>
    >>> This could still be made to work, even for the generic case. Can you
    >>> tell us more about the context? There's a short cut if you are using
    >>> the Proxy class, otherwise you'll have to go about it the longer way.

    >>
    >> How would that longer way look like?
    >>
    >> About the context I just wrote in another reply, so I don't repeat it
    >> here.
    >>
    >> There must be a way, because the combination of Java compiler and runtime
    >> system does exactly the same.

    >
    > One option would be to sidestep the problem of reproducing what the
    > compiler does, and just use the compiler. Could you generate little source
    > fragments at runtime with the relevant inputs, then compile them to
    > bytecode on the fly? If you had an interface like:
    >
    > public interface Invoker {
    > public Object invoke(Object receiver, Object... params);
    > }
    >
    > Then when you're in the situation that the receiver is a
    > javax.swing.JPanel (i'm guessing here), the method is called "add", and
    > there is one parameter, a javax.swing.JTextArea, you would generate code
    > like this (from a very simple template - note that all the types are based
    > on the runtime class of the parameters you happen to have at the time,
    > which is easily determined):
    >
    > class javax$swing$JPanel_add_javax$swing$JTextArea implements Invoker {
    > public Object invoke(Object receiver, Object... params) {
    > javax.swing.JPanel castReceiver = (javax.swing.JPanel)receiver;
    > if (params.length != 1) throw new IllegalArgumentException(); // or
    > something more appropriate
    > javax.swing.JTextArea param0 = (javax.swing.JTextArea)params[0];
    > // you also need to do something about exceptions
    > return castReceiver.add(param0);
    > }
    > }
    >
    > Compile it, put the generated class file on your classpath,


    Or use a custom classloader that knows about all the invokers you've
    generated. It could probably also be the cache you describe below.

    > then load it and use it. You would keep a global cache of generated
    > invokers, and reuse them rather than generating them afresh where you
    > could.


    Clever. I think Invoker.invoke() throws InvocationException, by the way.
    Mike Schilling, Nov 28, 2010
    #17
  18. Thanks Mike!

    Mike Schilling <> wrote:
    > http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.
    > ...
    > 0. Given the object O you want to call the method on, the method name M,
    > and a list of arguments A:
    > 1. Call O.getClass().getMethods() to find all the public methods callable on
    > O.
    > 2. Reject the ones not named M
    > 3. Reject the ones with the wrong number of arguments. (Be careful here if
    > you're going to support variable-argument methods)


    I see. My assumption that getMethod() handles the lookup more
    dynamically was wrong.
    Alexander Burger, Nov 28, 2010
    #18
  19. Thanks markspace!

    markspace <> wrote:
    > The only way to do this is to search the method signatures. Probably
    > something you could have done, but I promised help, so here it is. Note


    That's very nice! You saved me a lot of time digging and experimenting.

    > this is a first fit algorithm, not a best fit. If there are multiple
    > matches for signatures (i.e., the method name is overloaded) we take the
    > first one that works.


    Thanks also for the other post checking for multiple candidates! For now
    I went with the first-fit way. Also, I use isAssignableFrom() instead of
    isInstance(), because primitive type arguments must also be handled, and
    an array of classes is already prepared by the interpreter. Constructors
    are handled in a similar way.
    Alexander Burger, Nov 28, 2010
    #19
  20. Tom Anderson <> wrote:
    >> There must be a way, because the combination of Java compiler and
    >> runtime system does exactly the same.

    >
    > One option would be to sidestep the problem of reproducing what the
    > compiler does, and just use the compiler. Could you generate little source
    > fragments at runtime with the relevant inputs, then compile them to
    > bytecode on the fly?


    Yes, good idea. PicoLisp does similar things in the C and asm versions
    calling the C compiler dynamically. I'll keep that option in mind.
    Alexander Burger, Nov 28, 2010
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ewolfman
    Replies:
    4
    Views:
    669
    ewolfman
    Nov 15, 2005
  2. Replies:
    3
    Views:
    12,237
  3. Joe Weinstein
    Replies:
    2
    Views:
    528
    Chris Uppal
    Mar 18, 2006
  4. fAnSKyer

    getmethod in reflection

    fAnSKyer, Oct 24, 2006, in forum: Java
    Replies:
    4
    Views:
    498
    Mike Schilling
    Oct 25, 2006
  5. chucky
    Replies:
    14
    Views:
    1,286
    Twisted
    Aug 2, 2007
Loading...

Share This Page