How to call main method on class identified by String - java

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.

Related

Identifying the class type for arguments required in gerDeclaredMethod in java

I am using Java Reflection to invoke a method xyz with these 4 parameter types:
Set<LineItems>
String
Document
Profiler
While using getDeclaredMethod I am required to give the method argument type. If String's argument type is String.class, what should be done for Set<LineItems>? Should it be Set.class? (Keeping in mind that Set is an interface)
The function xyz is a default function in interface abc. This interface is being implemented in multiple other classes, so method should be reflected here only. Here is my code:
Class c = abc.getClass();
Class args[] = new Class[4];
args[0] = Set.class;
args[1] = String.class;
args[2] = Document.class;
args[3] = Profiler.class;
Method m = c.getMethod("xyz",args) ;
I'm getting the NoSuchMethodFoundException
I know there could be multiple flaws in this. Any help?
what should be done for Set<LineItems>?
What you have there Set.class should do. Remember that Java generics come with type erasure, therefore, at runtime, the signature of that method will say Set.class, too!
And yes, when the signature of the method you intend to call says Set, then using the Set (interface) class is exactly what you have to do. If for example, the method signature said HashSet then Set.class would not do!
I was able to solve this by :
Object[] args= new Object[4];
args[0]= setOfItems;
args[1]= aString;
args[2]= document;
args[3]= profiler;
Method m = this.getClass().getMethod("xyz", Set.class, String.class, Document.class, Profiler.class);
Object result = proxy.invoke(m, this, args)

Convert an Object[] to a Class<?> type at runtime?

I am having deep trouble and this is advanced Java (I am using Reflection API).
Two questions.
I have the following:
Class<?> clazz = String.class;
Object[] values = new Object[] { "abc", 50L, 20 } // notice the different types
I want to be able to Object[] -> clazz[]. Why am I asking this? Because the type of "clazz" is known at runtime via reflection.
// This works
String[] params = Arrays.asList(values).toArray(new String[values.length]);
// This doesnt
String[] params = Arrays.asList(values).toArray(new clazz[values.length]);
There's something else: Let's say that, at runtime, the class is java.lang.String. In the middle of the creating of the array above, how can I use String.valueOf() on each element?
How can I achieve this?
Thank you!
Explaning more what I am trying to achieve
I have this class:
class A {
public void doIt(String... params) {
...
}
}
And also this class:
class B {
public void doIt(Long... params) {
...
}
}
As you can see, A and B have the same method doIt() but with different argument types.
I the code below will fail:
Object[] params = {1, 2, 3};
new A().doIt(params);
With the exception:
java.lang.ClassCastException: java.lang.Object[] cannot be cast to java.lang.String[]
So, what I am trying to do, is find the type of doIt()'s first param and I am trying to convert Object[] to String/Long/Integer[]. Is is more clear now?
Skipping the comments on whether that kind of coding yields the best program, but answering your very question...
You cannot do new clazz[] as new in Java takes a class name, not an expression which evaluates to a Class object.
More pragmatically: new String[10] is not the same as new String.class[10]. The latter is what your example does. And is simply not supported.
You can do Array.newInstance(clazz, values.length), that seems like it would be what you are trying to do. But I don't see how it helps you. If the elements of the array are of different types, and you end up with an array of Strings (albeit dynamically created), you won't be able to put all of the elements into it anyhow.
Are you looking for Array.newInstance(Class<?>, int)? That's the way to reflectively build an array with a Class object rather than a compile-time type.
You cannot write new clazz[values.length], but you can write Array.newInstance(clazz, values.length).

Why warning when trying to reflection on empty argument function

I was trying to use reflection to call funcA() of a class ClsA. However Eclipse Juno is showing a warning in TestA class remark with warning (A) as shown below.
The ClsA is like this:
public class ClsA {
String varA;
...
...
private String funcA() {
return varA;
}
}
This is the code I use the reflection call on funcA():
public class TestA {
public static void main(String[] args) {
ClsA clsA = new ClsA();
Class noparam[] = {};
Method funcA;
String retStr;
funcA = ClsA.class.getDeclaredMethod("funcA", noparam);
funcA.setAccessible(true);
retStr = (String) funcA.invoke(clsA, null); // warning (A)
}
}
And this is the warning I get. Basically I just don't really understand what is the message that warning trying to bring? How could I explicitly cast a null?
The argument of type null should explicitly be cast to Object[] for
the invocation of the varargs method invoke(Object, Object...) from
type Method. It could alternatively be cast to Object for a varargs
invocation
There can be 2 types of method invocation, one is to call with fixed argument, other is to call with variable arguments.
If you are providing just null as argument, it is not clear to java whether that method is for variable parameters or no parameter at all (as variable parameters may also accept no agrument).
So it asks to mention explicitly like (Object[])null or (Object)null, even if you don't want to provide any argument.
Invoking Methods
Since the method signature is (Object obj, Object... vars) unless you declare the null cast the most forward approach is to not include any arguments.
Example from your code above:
retStr = (String) funcA.invoke(clsA);
Anytime there are VarArgs (Something...) it means 0 or more.

How is the Java compiler able to distinguish between those two constructors/methods?

public class MyClass {
private String string;
private Object[] objects;
// constructor 1
public MyClass(String string, Object... objects) {
this.string = string;
this.objects = objects;
}
// constructor 2
public MyClass(String string) {
this.string = string;
}
public static void main(String[] args) {
MyClass myClass = new MyClass("foobar");
}
}
In that case, how did the Java compiler decide to use constructor 2 instead of constructor 1? Why no The constructor ... is ambiguous or a similar error occurs?
PS: the question works with classic methods too.
A var-args method/constructor will be chosen only if there is no non-var-arg method/constructor. So it is clear that why compiler chooses MyClass(String string).
It's always the most specific method that is called.
new MyClass("foobar");
searches to call that constructor which takes an object of type String as it's only argument.
and, var-args method will be used iff matching non-var-args method doesn't exist.
As I understand varargs constructors and methods are only syntax sugar, which transforms to array declarations. So your constructor 1 during compilation would be nearly equal to:
public MyClass(String string, Object[] objects) {
this.string = string;
this.objects = objects;
}
It means, that if you want to construct instance of MyClass by following code:
MyClass obj = new MyClass("Hello", "1", "2");
It would be equal to:
MyClass obj = new MyClass("Hello", new Object[]{"1", "2"} );
The JVM will look for Exact matching for passing values to variables in methods/constructor, if it's not able find exact match it will treat the values as Object.
jls-15.12.2 states that compiler will first look best match for without autoboxing or var agrs. Constructor #2 fits in your case.
If it had not been there, the first autoboxing will be applied, i.e. any method with parameter super class of String i.e. Object will be callled.
// constructor 2
public MyClass(Object string) {
this.string = string.toString();
}
Now, even after applying autoboxing, compiler is not able to find best match, it will go for var args. So, if you remove constructor 2 from your code, first constructor will be called.

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.

Categories

Resources