Is it possible to postpone function call binds to runtime in Java? - 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.

Related

Is it possible to create an Object when you have the Type as a String? [duplicate]

I was looking as the question : Instantiate a class from its string name which describes how to instantiate a class when having its name. Is there a way to do it in Java? I will have the package name and class name and I need to be able to create an object having that particular name.
Two ways:
Method 1 - only for classes having a no-arg constructor
If your class has a no-arg constructor, you can get a Class object using Class.forName() and use the newInstance() method to create an instance (though beware that this method is often considered evil because it can defeat Java's checked exceptions).
For example:
Class<?> clazz = Class.forName("java.util.Date");
Object date = clazz.newInstance();
Method 2
An alternative safer approach which also works if the class doesn't have any no-arg constructors is to query your class object to get its Constructor object and call a newInstance() method on this object:
Class<?> clazz = Class.forName("com.foo.MyClass");
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
Object instance = constructor.newInstance("stringparam", 42);
Both methods are known as reflection. You will typically have to catch the various exceptions which can occur, including things like:
the JVM can't find or can't load your class
the class you're trying to instantiate doesn't have the right sort of constructors
the constructor itself threw an exception
the constructor you're trying to invoke isn't public
a security manager has been installed and is preventing reflection from occurring
MyClass myInstance = (MyClass) Class.forName("MyClass").newInstance();
Using newInstance() directly is deprecated as of Java 8. You need to use Class.getDeclaredConstructor(...).newInstance(...) with the corresponding exceptions.
To make it easier to get the fully qualified name of a class in order to create an instance using Class.forName(...), one could use the Class.getName() method. Something like:
class ObjectMaker {
// Constructor, fields, initialization, etc...
public Object makeObject(Class<?> clazz) {
Object o = null;
try {
o = Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException e) {
// There may be other exceptions to throw here,
// but I'm writing this from memory.
e.printStackTrace();
}
return o;
}
}
Then you can cast the object you get back to whatever class you pass to makeObject(...):
Data d = (Data) objectMaker.makeObject(Data.class);
use Class.forName("String name of class").newInstance();
Class.forName("A").newInstance();
This will cause class named A initialized.
Use java reflection
Creating New Objects
There is no equivalent to method invocation for constructors, because invoking a constructor is equivalent to creating a new object (to be the most precise, creating a new object involves both memory allocation and object construction). So the nearest equivalent to the previous example is to say:
import java.lang.reflect.*;
public class constructor2 {
public constructor2()
{
}
public constructor2(int a, int b)
{
System.out.println(
"a = " + a + " b = " + b);
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct
= cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
which finds a constructor that handles the specified parameter types and invokes it, to create a new instance of the object. The value of this approach is that it's purely dynamic, with constructor lookup and invocation at execution time, rather than at compilation time.
Class.forName("ClassName") will solve your purpose.
Class class1 = Class.forName(ClassName);
Object object1 = class1.newInstance();
String str = (String)Class.forName("java.lang.String").newInstance();
something like this should work...
String name = "Test2";//Name of the class
Class myClass = Class.forName(name);
Object o = myClass.newInstance();

Java Reflection Enumerate Type in GetMethod

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.

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.

Pass method as parameter for another method

I want to do something like this:
public class Myclass() {
ArrayList<Object> objects;
public Myclass() {
this.objects = new ArrayList<>();
}
public ArrayList<Object> callObjectAndMethod(Method method, String string) {
ArrayList<Object> returnlist = new ArrayList<>();
// call method for given object reference. I also assume that return value of the method is String
String output = object.method();
for (Object object : this.objects) {
if (object.method.contains(string)) {
returnlist.add(object.method());
}
return returnlist;
}
}
Is it possible to pass method as parameter for another method ? Also i would like to do this sort of iteration for multiple variable types. int,String,double is there way to create method that works with multiple input types. (probably not) but i am only trying to reduce the amount of lines i have to write. i will have a lot to write if i write this thing for all the methods i want to compare or use copy-paste technique and change little things (which is not good).
You could use java-8 functional Programming. It allows using Function Interfaces to create such implementations called lambda expressions. These lambda expressions could be passed as variables to other methods. For example
Predicate isLengthTen = string -> string.length() == 10;
Now this could be passed as a parameter to any method. Hence, you could use java 8 functional programming easily to pass the code as parameter basically.
boolean testTruth(Predicate<T> predicate,T dataUnderTest){
predicate.test(dataUnderTest);
}
Hope it helps!
Quick read about how to use is here

Using getDeclaredMethod to get a private method of any super (parent) class

I need to call a private method via reflection. In advance I don't kow if that private method is part of the specified class or a super class. I have the name of the private method, their parameters and their parameter types.
My first approach was the following:
Object classToTest = ....;
ArrayList<Method> methods = new ArrayList<Method>();
Class<?> classIndex = classToTest.getClass();
//iterate over (super) classes to collect all methods
do{
methods.addAll(Arrays.asList(classIndex.getDeclaredMethods()));
classIndex = classIndex.getSuperclass();
}
while(classIndex.getClass()!=null);
for(Method method : methods){
//match the right method
}
//call it
method.setAccessible(true);
method.invoke(classToTest,parameterValues);
Problem with that approach: Getting the right method out of the list is not trivial as the sourcecode for .getDeclaredMethod(...) shows. Unfortunatly many private internal methods are used which thus cant't be reused...
The 2nd approach was to use the getDeclaredMethod() which does the matching for me:
Method method=null;
Class<?> classIndex = classToTest.getClass();
//iterate over (super) classes since private method may be inherited
do{
//exception catching as part of the normal code is ugly
try{
method = classIndex.getDeclaredMethod(nameOfMethod, parameterTypes);
//method found thus exit
break;
}
catch(NoSuchMethodException nsme){
//method not found thus check super class
classIndex = classIndex.getSuperclass();
}
}
while(classIndex!=null);
if(method==null) throw new NoSuchMethodException( classToTest.getClass().getName() + "." + nameOfMethod + Arrays.toString(parameterValues));
//no method matching necessary
method.setAccessible(true);
method.invoke(classToTest,parameterValues);
Disadvantage of that approach: the exception catching as part of the code flow is ugly - however I currently don't see an alternative without reimplementing the matching code of Class.java.
So does anyone see an alternative approach to get the right private method?

Categories

Resources