Java, Reflection, getting a Method from a Class - java

I'm having some trouble using reflection in Java. I'm attempting to save a method of a data structure but getting an error. The error is
java.lang.NoSuchMethodException: cs671.eval.SerialList.add(java.lang.Integer, java.lang.String)
The method, in this case, that I'm trying to get is the add method for a SerialList that takes a Comparable and an Object as its parameters.
structType = "cs671.eval.SerialList", keyType = "java.lang.Integer", and valType = "java.lang.String" are strings that were read in from a file.
Class dataClass = null, comparableClass = null, objectClass = null;
try{ // create data structure
dataClass = Class.forName(structType);
comparableClass = Class.forName(keyType);
objectClass = Class.forName(valType);
}
catch(ClassNotFoundException e){}
java.lang.Object structObj = null;
try{ // Create a data structure object
structObj = dataClass.newInstance();
}
catch(Exception e){}
Method m = null;
try{ // Attempt to get add method for the data structure
m = dataClass.getMethod("add", comparableClass, objectClass); // This is where it fails
}
catch(Exception e){}
Basically I'm trying to get the right method on the right datastructure with the correct classes that are going to get passed into that method but I don't know how to tell the getMethod method that those classes (comparableClass and objectClass) are the correct ones.
Thanks in advance!
Added: Here's the SerialList's add method signature
public void add(java.lang.Comparable, java.lang.Object)

You are saying -
The method, in this case, that I'm trying to get is the add method for a SerialList that takes a Comparable and an Object as its parameters.
But passing the classes - java.lang.Integer, java.lang.String.
Just a note - Only public methods are visible to getMethod() for non-publics you would have to use getDeclaredMethod() instead.

From http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#getMethod%28java.lang.String,%20java.lang.Class...%29:
To find a matching method in a class C: If C declares exactly one public method with the specified name and exactly the same formal parameter types, that is the method reflected. If more than one such method is found in C, and one of these methods has a return type that is more specific than any of the others, that method is reflected; otherwise one of the methods is chosen arbitrarily.
=> You need to pass java.lang.Comparable.class & java.lang.Object.class

Apologies for providing wrong answer earlier. Based on your comments it appears that you're trying to get a Method by avoiding to provide specific parameter types needed in the signature of that method.
If my understanding is correct then you should rather use Class#getMethods() and examine the returned Method[] for your method. Consider a skeleton code like this:
Method[] methods = dataClass.getMethods();
boolean matched = false;
// find a matching method by name
for (Method method: methods) {
Class<?>[] parameterTypes = method.getParameterTypes();
if ( "add".equals(method.getName()) && parameterTypes.length == 2 ) {
// method is your target method
// however you'll need more strict checks if there can be another add method
// in your class with 2 different parameter types
}
}

As other answers have stated, to use getMethod() you need to know and use the actual declared formal parameters of the method you are attempting to retrieve.
However, if for some reason you do not know the formal parameters at compile time, then you can iterate over all of the methods from the class until you find a method that fits your parameters (or find the most specific method that fits your parameters).
There is functionality written to do this already in apache commons bean utils, specifically in org.apache.commons.beanutils.MethodUtils.invokeMethod(...) and MethodUtils.getMatchingAccessibleMethod(...).
Source code for the above methods can be easily viewed online here.

Related

How to prune variations of same method obtained by use of Java reflection?

I'm using reflection discover a method satisfying some conditions and to invoke the found method.
Check following code. Using Groovy..
class TestClass<T>{
T hello(){
return null
}
}
class TestSubClass extends TestClass<List<String>>{
List<String> hello(){
return null
}
}
TestSubClass.methods.each{
if(it.name.contains("hello")){
println it.toGenericString()
}
}
which prints out
public java.util.List<java.lang.String> TestSubClass.hello() // <-- most relevant method for a user of this class
public java.lang.Object TestSubClass.hello()
public java.lang.Object TestSubClass.super$2$hello()
Java reflection is returning multiple declarations of same method based on inheritance/generics, which is understandable.
In my case, I'd like to discover the method with most appropriate signature, including exact type of returnTypes. For example, in the above example, the 1st method in the output has full signature and that's the one we'd usually invoke (without reflection).
Note: above is a simplified example. The real logic is not about finding methods based on naming.
The compiler generates the other 2 methods. Luckily, there is a property that you can check to see this: synthetic:
TestSubClass.declaredMethods.each{
if(it.name.contains("hello") && !it.synthetic) {
println it.toGenericString()
}
}
Which now prints just:
public java.util.List<java.lang.String> test.TestSubClass.hello()
The Java specifications require a method to marked synthetic if it is not explicitly in the source code.
A construct emitted by a Java compiler must be marked as synthetic if
it does not correspond to a construct declared explicitly or
implicitly in source code, unless the emitted construct is a class
initialization method (JVMS ยง2.9).
JAVA specifications
You can try:
TestSubClass.methods.each{
if(it.name.contains("hello") && !m.isSynthetic()){
println it
}
}
You can also check against if the method is bridged. Which is a similar concept:
https://stackoverflow.com/a/5007394/1754020
In my case, I'd like to discover the method with most appropriate
signature, including exact type of return Types.
If it's the Java API that you're wondering about, then you'll want to look at class Class. It contains a large number of reflective methods that allow you to interrogate types.
For example, the following code fragment searches all the methods declared on a supplied type for one method which: takes no arguments, is public and static, and has a return type of DateSerial.Config...
public static <D extends DateSerial<?>> DateSerial.Config<D> obtainMetadata(Class<D> cls) {
Method exe = Stream.of(cls.getDeclaredMethods())
.filter(m -> m.getParameterCount() == 0 &&
m.getReturnType() == DateSerial.Config.class)
.filter(m -> {
int mod = m.getModifiers();
return Modifier.isStatic(mod) && Modifier.isPublic(mod);
})
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"No metadata accessor for " + cls.getName()));
:
:
}
You can get as precise with your interrogations as you need. For example, you can filter methods based on those with a certain number of arguments, the last of which is a String[] array, etc. etc. Caveat emptor: Java reflective code is verbose, ugly, and can be hard to read.

How to call method of a unknown object

I want to do something different with java reflection. The program i written to add global listeners to java components when applets are opened from browser. An event fired and i get event source object. Here i don't know the actual class name that object referring to.
if(object.getClass.getName().contains("oracle.ewt.laf.basic.BasicTabBarUI$Menu"))
{
// here we can invoke methods,fields,etc using reflection
}
I can call the methods of BasicTabBarUI$Menu class with reflection.
Suppose now i have the following lines with me in the above if block
LWMenuItem menuItem = (LWMenuItem)object;
menuItem.getLabel());
I don't want to specify LWMenuItem class name , instead i want to call its method getLabel(). If we know the class name , we can do as above. But how can we do same with reflection. How can we do casting in reflection?
You don't need to do casting, except of the result of calling the method. Just use the object's Class object, which has a getMethod method that will return a Method object for the method you want, then invoke it:
Class cls = object.getClass();
Method getLabel = cls.getMethod("getLabel", null);
String label = (String)getLabel.invoke(object, null);
You can continue working with the basic object when using the return value from getLabel():
Method getLabelMethod = object.getClass().getMethod("getLabel");
Object menuItem = getLabelMethod.invoke(object);
menuItem.getClass().getMethod("getName").invoke(menuItem); // or whatever...

Generic method calling using java reflection api

I have been trying to develop an application. A bean script will be written as per requirement which in turn will call methods (defined in the application) in various order as per requirement. The application code (apart for bean script) would not be changed.
Also, the application uses external jars which provide large number of methods - of which some are implemented in the application. However, I would like to have the possibility to use the other methods (ones that are not yet implemented) without making changes to application should the requirement arise. For this, I would like to use the Java reflection API. The user should be able to call any method present in the external jars by passing the method name and corresponding parameters (using the the external jar documentation).
I'm a java newbie so I have some code that tries to achieve it (may not be syntactically correct):
public void callExternalJarMethod(String methodName, Class[] methodParameterTypes, Object[] methodParameters)
throws NoSuchMethodException {
String className = "SampleClassName";
Class classObject = Class.forName(className);
Method methodObject;
if (methodParameterTypes.length == 0) {
methodObject = classObject.getMethod(methodName, null);
} else {
methodObject = classObject.getMethod(methodName, methodParameterTypes);
}
// Not handling calling of static methods in which case "null" would be passed instead of methodObject to the invoke method
Object returnValue = methodObject.invoke(methodObject, methodParameters);
}
I'm trying to find a way I can get the Class[] methodParameterTypes, and Object[] methodParameters populated with the relevant values. I would have the parameter types and parameter values as string. Also, any pointers towards useful utils would be appreciated.
Thanks in advance.
You are not passing an instance of SampleClassName to the Method.invoke() call here...
Object returnValue = methodObject.invoke(methodObject, methodParameters);
If the method you are going to invoke is static, you can do this...
Object returnValue = methodObject.invoke(null, methodParameters);
Otherwise (non-static), you need to create an instance of SampleClassName to execute the method on.
If the class does not need any constructor arguments, you could use...
Object returnValue = methodObject.invoke(classObject.newInstance(), methodParameters);
(Obviously there will be a load of Exceptions that you need to handle by doing "newInstance" and "invoke"...)

Getting the best fit Instance Method in Java

If I run the following program:
class Runit{
public static void main(String[] argsWut) throws Exception {
String arg = "what?";
Class[] parameters = { new Object().getClass() };
Object[] args = { arg };
System.out.println("".getClass().getMethod("equals",parameters).invoke("what?",args));
}
};
I get the following on the command line:
true
On the other hand, if I modify the parameters line a little:
class Runit{
public static void main(String[] argsWut) throws Exception {
String arg = "what?";
Class[] parameters = { arg.getClass() }; // changed a little here so it's a bit more dynamic --
Object[] args = { arg };
System.out.println("".getClass().getMethod("equals",parameters).invoke("what?",args));
}
};
I get:
Exception in thread "main" java.lang.NoSuchMethodException: java.lang.String.equals(java.lang.String)
at java.lang.Class.getMethod(Class.java:1605)
at test.Runit.main(Runit.java:7)
From this one example it looks to me as though the getMethod method only works with exact parameters. Is there a way to get some form of a "best fit" method? e.g. If an exact match exists, it would return that method, but if no exact match exists, it can return any method that could accept my given arguments.
You may have better luck with the Apache Commons Lang MethodUtils class, which has a method "invokeMethod" that uses the target arguments for the method to narrow down the appropriate type (i.e., you don't have to tell it the parameter type).
This seems to work:
System.out.println(MethodUtils.invokeMethod("what?", "equals", new Object[] {"what?"}));
See javadocs for more details: http://commons.apache.org/lang/api/org/apache/commons/lang3/reflect/MethodUtils.html#invokeMethod(java.lang.Object,%20java.lang.String,%20java.lang.Object...)
You may be interested in Commons BeanUtils Apache library, specifically this method:
http://commons.apache.org/beanutils/v1.8.0/apidocs/org/apache/commons/beanutils/MethodUtils.html#getMatchingAccessibleMethod%28java.lang.Class,%20java.lang.String,%20java.lang.Class%5b%5d%29
Hope this helps
From the documentation for getMethod():
To find a matching method in a class C: If C declares exactly one
public method with the specified name and exactly the same formal
parameter types, that is the method reflected. If more than one such
method is found in C, and one of these methods has a return type that
is more specific than any of the others, that method is reflected;
otherwise one of the methods is chosen arbitrarily.
(Emphasis mine.)
What you are asking for is to have reflection perform overload resolution for you. And apparently it won't. If you really need this functionality, you can either 1) give up on using reflection and invoke the method directly, or 2) if that's not possible, look up the rules for overload resolution in Java (you could start here), use getMethods() to determine the available methods, and then perform overload resolution manually. Fun times, I know.
Edit: As other answerers have pointed out, someone has already taken the time to do that for you. Cool!

Utilising Javassist to provaide overloading based on return type

Ive used Javassist to dynamically change the return type of a function call, but its not working.
Ive got a call that is defined in the source code as simply:
Boolean getResult(){return true;}
But then at run time I dynamically change it to:
String getResult(){return "true"}
I then call it as:
Object o = myobject.getResult();
And get a MethodNotFound exception. If I use reflection I can see my new method on the object, but the call is failing, apparently because its somehow bound to the old return type.
If I call the new method reflectively (slight pseudocode..):
Method m = myobject.getClass.GetDeclaredMethods().(...find method named GetResult...)
Object o = m.invoke(myObject);
Then all works fine, and I can switch between manipulated and non manipulated byte code without issue, and I can see that the type of O is either String or Boolean accordingly.
Any ideas why?

Categories

Resources