Java Reflection Enumerate Type in GetMethod - java

I'm trying to call a method by name at runtime with reflection.
But I need to make this compatible with all methods.
I'll explain :
I have a text with this current format : methodname|param1;param2;etc...
Now i want this text split, and find the method in my class.
try {
String methodName = message.substring(0,message.indexOf("|"));
method = ServerManager.class.getMethod(methodName, HERE I DONT KNOW WHAT TO PUT);
//This code not tested yet//
if(method != null)
{
method.invoke(ServerManager.class,message.split("|")[1].split(";"));
}
}
catch (NoSuchMethodException e) { e.printStackTrace(); }
To find the method, I need to put the type. But number of parameters and the name of the method change. How can I give to getMethod an array of my parameters type ?
I've tryied to enumerate all getClass() of each parameters but getMethod seems not accepting array in second parameters.
Sorry for my bad english, and thank you for help.

Remove the class.getClass() and then you can pass an array of Class, for instance:
method = ServerManager.class
.getMethod(methodName, new Class[]{Integer.class, String.class});
or simply:
ServerManager.class.getMethod(methodName, Integer.class, String.class);
the array will be implicitly added there by Java compiler.

Related

How to call main method on class identified by String

I'm trying to call the main method on a Java class identified by a String. In the actual program, the class identifier will be a variable, but for the moment I'm trying this. The desired parameter main, newargs, is of type String[].
try {
Class c = Class.forName("Arena");
Class[] aarg = new Class[1];
aarg[0] = String[].class;
Method m = c.getMethod("main", aarg); // Needs parameter class list
m.invoke(newargs);
}
catch (Exception e) {
System.err.println(e);
}
First, I get a compiler warning (Athena is the name of the class I'm currently working in):
Note: Athena.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Then on testing I get this exception:
java.lang.IllegalArgumentException: wrong number of arguments
I feel like the primary thing is I don't know how to identify the parameter of main() as an array of the String class (failed to find an example of that), but maybe I'm missing more than that.
In getMethod() you supply an array of Classes with length 1. This is somewhat incorrect; it should work due to the use of varargs, but it's pointless. The method will interpret it the same as simply providing one class. Literally adding .class to the expected types and separating them with commas should yield the proper result. If you had String[] and int as parameters in main(), it would look like c.getMethod("main", String[].class, int.class).
In invoke(), you can ignore the first parameter and pass null since main() is a static method. The second is declared as a varargs parameter, but in my test this didn't work properly. I cast to a single Object instead of an array of length 1 of Objects. Here, Object works as a raw type since invoke() is uninformed as to what its parameters should be at compile time, but can cast them to the desired type at runtime.
The varargs use in invoke() doesn't work in this case because it expects the type Object[] or comma separated values that can be combined to an Object[]. The issue is that String[] is a subclass of Object[]. So instead of interpreting String[] as a single String[] parameter, the invoke() method thinks you are trying to give it a set of distinct String/Object inputs.
Internal logic:
method.invoke(null, 1, 2, 3) becomes Object[] with int elements. 3 separate int parameters
method.invoke(null, "hello", "wow", "ok") becomes Object[] with String elements. 3 separate String parameters
method.invoke(null, String[] { "hello", "wow", "ok" }) becomes Object[] with String elements. 3 separate String parameters, even though we only desire one parameter that is String[]
Honestly this isn't your fault, the API is lacking good documentation to diagnose these issues. Feel free to ask me questions, this stuff is confusing.
try {
Class<?> c = Class.forName("Test");
Method m = c.getMethod("main", String[].class); // Needs parameter class list
String[] input = new String[] { "hello world" };
m.invoke(null, (Object) input);
}
catch (Exception e) {
e.printStackTrace();
}
i think, it would work if type of aarg was String[]. It is Class[] now.
invoke accepts two arguments,
public Object invoke(Object obj, Object... args)
obj the object the underlying method is invoked from args the
args arguments used for the method call
Class c = Class.forName("Arena");
String[] params = null;
Method m = c.getMethod("main", String[].class); // Needs parameter class list
m.invoke(null,(Object) params);
Note null for invoke as main method don’t need instance.
Class<? extends Demo> clazz = Demo.class;
Method mainMethod = clazz.getDeclaredMethod("main", String[].class);
final Object[] args = new Object[1];
args[0] = new String[]{"1", "2"};
mainMethod.invoke(null, args);
We can use any class name instead of Demo.

Java Generics AnyType, how do I allow any method to be called?

Given:
public<?> void methodName(? input){
var something = ?.GetItNow();
}
We have a ton of classes that were auto-generated from WSDL. Almost all of them have the same methods, but there is no way to have them implement an interface for those same methods. Assume all these different classes have a method named GetItNow(). My question is how do I set up a Generic method to allow the code above to work?
When using the "?" do I have to always use the "extends" key-word? If so how do I extend any object so I don't see compile errors that "method doesn't exist.
Note the code above is for illustration purposes only and does not do anything (yet).
You could use reflection to access the method, provided the name is known beforehand.
public void methodName(Object input){
try{
Method method = input.getClass().getMethod("GetItNow");
RETURN_TYPE returnValue = (RETURN_TYPE)method.invoke(input);
}catch(Exception e){
throw new RuntimeException("Error while invoking method",e);
}
}

Is it possible to postpone function call binds to runtime in Java?

Consider a simple case where I ask a user to select a fruit among 10 different fruits. Say the fruits are, apples, oranges, mangoes,... etc., If the user selects apples, I call apples(), if he selects mangoes, I call mangoes() and so on...
To select which function is to be called, I DON'T want to use a switch or if-else statements. How do I select which function is to be called during run-time?
NOTE : The programming language I am using is Java
Use java Refection api to call function at runtime.
Class noparams[] = {};
Class cls = Class.forName("com.test.Fruit");
Object obj = cls.newInstance();
//call the printIt method
Method method = cls.getDeclaredMethod("apples", noparams);
method.invoke(obj, null);
Use Reflection.
eg:
Write all the functions in a class ; say com.sample.FruitStall
Then use below code.
String className = "com.sample.FruitStall";
String methodName = "apple"; //here you will choose desired method
Object result;
Class<?> _class;
try {
_class = Class.forName(className);
} catch (Exception e) {
e.printStackTrace();
}
Object[] args = new Object[1]; // To Supply arguments to function
result = _class.invokeMethod(methodName, args);
use the design pattern "Command".
http://www.codeproject.com/Articles/186192/Command-Design-Pattern
hiding the details of the action it needs to perform.

How to get a Method instance for call stack element *without* arg signature?

Disclaimer: I have 10 years' experience programming, but 8 of which is in PHP (loosely typed) -- I have been using Java now for 4 days :)
In java, I need to get the value of an annotation for a method in the call stack. As far as I can tell, I do this with the Method object. From the call stack, I have retrieved the names of the class and method (strings). This is the (abbreviated) code that I am using...
Calling Method:
public class myClass
{
#Path( "some/path/value" )
public void myMethod( String someArg ) { ... }
}
Retrieval Code:
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
String callingMethodName = trace[depth].getMethodName();
String callingClassName = trace[depth].getClassName();
Class[] signature = new Class[1];
signature[0] = String.class;
Class callingClass = Class.forName( callingClassName );
Method callingMethod = callingClass.getMethod( callingMethodName, signature );
Path annotation = callingMethod.getAnnotation( Path.class );
This works like a charm, successfully returning the value of the #Path annotation ("some/path/value")
However, if you notice, I had to supply a signature of the method I was looking for. As you can see in the class code, there is only 1 method with that name, so, theoretically, the signature of the method should be irrelevant, right? As far as I can tell from various docs/blogs/examples, I should be able to call getMethod( ) with either no 2nd argument, or with null for the 2nd argument, but if I use the following:
Class callingClass = Class.forName( callingClassName );
Method callingMethod = callingClass.getMethod( callingMethodName );
Or even:
Class callingClass = Class.forName( callingClassName );
Method callingMethod = callingClass.getMethod( callingMethodName, null );
I get a NoSuchMethodException. Am I doing something wrong here? Should I be taking a different approach altogether?
In this particular situation, the calling class/method never uses polymorphism so there is only ever 1 signature. However, said signature is not known (unless that can also be determined by data available/derivable from Thread.currentThread( )), so I need a way to get a Method object without knowing the signature.
No. The signature matters. If you don't know the exact signature, but know the name, get all the methods and iterate over them until you get a hit:
Method method = null;
for (Method m : c.getDeclaredMethods()) {
if (m.getName().equals(callingMethodName)) {
method = m;
break;
}
}
// variable "method" is the first that matched name, or null if not found
Also, since java 1.5, Class.getMethod(String, Class...) is a varargs method, so you don't need the java cruft that both the question and other answer has, ie:
This works, but avoid this crap (the old, hard way):
Class[] signature = new Class[1];
signature[0] = String.class;
Method callingMethod = callingClass.getMethod( callingMethodName, signature);
Prefer this (new way):
Method callingMethod = callingClass.getMethod( callingMethodName, String.class);
If you want to match on the method name (without worrying about the arguments), you can do something like the following:
...
private Method findMethod (Class cls, String name)
{
for (Method method : cls.getDeclaredMethods( ))
if (method.getName( ).equals(name))
return method;
}
...
Method callingMethod = findMethod (callingClass, "myMethod");
Path annotation = callingMethod.getAnnotation(Path.class);
...
Basically just a linear search over all the methods in the class of interest until you find one with the name you're after.
your code:
Method callingMethod = callingClass.getMethod( callingMethodName, null );
is looking for a method named callingMethodName which takes no arguments. There's no magic that allows getMethod to figure out that your calls should return the first or only method with that name. They retrieve the method that matches exactly what you're asking for.
otherwise, you'd have to use .getMethods() and iterate over them to find the one you want?

Java Creating a new Class with Generics

I'm running into a problem with my program where given an object and an attribute name I want to return the method's return type.
public static Class<?> getAttributeType(Object object, String attributeName) {
try {
Method method = object.getClass().getMethod(
"get" + StringUtils.capitalize(attributeName));
return method.getReturnType();
} catch (Exception e) {
throw new RuntimeException("Unable to find the attribute:"
+ attributeName + " in class: "
+ object.getClass().getName());
}
}
This solution works great unless the return type of a method has a generic defined. For example if method prototype in question is List<Book> getBooks(), the code above would just return a List instead of a List<Book>. Is there any way for me to accomplish this? I can get the Generic type easily, I just don't know what to do with it once I have it.
Thanks in advance
To get the generic return type of a method, use the getGenericReturnType() method. This returns a Type, which you then test and down cast to a ParameterizedType which you can then query for the type parameters.
Not everything is possible via reflection, but is with a bytecode-library like ObjectWeb ASM.
The approach there is described in this article.

Categories

Resources