Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > getMethod() works and works not

Reply
Thread Tools

getMethod() works and works not

 
 
Alexander Burger
Guest
Posts: n/a
 
      11-27-2010
Alexander Burger <(E-Mail Removed)> 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.
 
Reply With Quote
 
 
 
 
Mike Schilling
Guest
Posts: n/a
 
      11-27-2010


"Alexander Burger" <(E-Mail Removed)> wrote in message
news:icripp$b4k$(E-Mail Removed)...
> 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/t...ons.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.

 
Reply With Quote
 
 
 
 
markspace
Guest
Posts: n/a
 
      11-27-2010
On 11/27/2010 10:24 AM, Alexander Burger wrote:

> Mike Schilling<(E-Mail Removed)> 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[i];
if( !(class1.isInstance( params[i] )) ) {
continue looking;
}
}
// all parameters check out here:
System.err.println( "Calling: " + m );
retVal = m.invoke( o, params );
}
}
}
return retVal;
}
}
 
Reply With Quote
 
markspace
Guest
Posts: n/a
 
      11-27-2010
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....

 
Reply With Quote
 
markspace
Guest
Posts: n/a
 
      11-27-2010
> 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[i];
if( !(class1.isInstance( params[i] )) ) {
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 );
}
}
 
Reply With Quote
 
Tom Anderson
Guest
Posts: n/a
 
      11-27-2010
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
 
Reply With Quote
 
Mike Schilling
Guest
Posts: n/a
 
      11-28-2010


"Tom Anderson" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) rth.li...
> 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.

 
Reply With Quote
 
Alexander Burger
Guest
Posts: n/a
 
      11-28-2010
Thanks Mike!

Mike Schilling <(E-Mail Removed)> wrote:
> http://java.sun.com/docs/books/jls/t...ons.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.
 
Reply With Quote
 
Alexander Burger
Guest
Posts: n/a
 
      11-28-2010
Thanks markspace!

markspace <(E-Mail Removed)> 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.
 
Reply With Quote
 
Alexander Burger
Guest
Posts: n/a
 
      11-28-2010
Tom Anderson <(E-Mail Removed)> 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.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
In a template, Eval () does not works in fields ofcontrols, it works abargaddon ASP .Net Web Controls 1 02-04-2008 09:16 PM
When I turn on my PC, it works, works, works. Problem! Fogar Computer Information 1 01-17-2006 12:57 AM
[py2exe.i18n] English works, German works, but not French. What do I miss? F. GEIGER Python 3 08-06-2004 10:01 AM
Webservice works once and then DOES NOT seem to work even though program does not crash Phi! ASP .Net Web Services 1 04-23-2004 08:42 AM
After rebooting my PC works, works, works! Antivirus problem? Adriano Computer Information 1 12-15-2003 05:30 AM



Advertisments