Calling a method with an argument trough reflection - java

I have the following code which allows me to input in the scanner the Employee getter method that I want to call and it will do it using reflection (the name of the method should not appear anywhere in the code). This works for getter methods but I now need to modify the code to do something similar for setter methods. I have been trying to figure how to do it for the past week but I have been unable. Any help would be appreciated.
Thanks.
public static void main(String[] args) {
Employee e = Employee.testEmployee(); // a sample employee
Class cls = e.getClass();
Scanner scanner = new Scanner (System.in); // to parse data the user types in
String nextCommand;
// until the user enters "quit", get the next input from the user, and if it matches
// a given command, get the desired information from the employee object
do {
System.out.print("Enter command >> ");
nextCommand = scanner.next();
Method method = null;
try{
method = cls.getMethod(nextCommand);
}
catch(NoSuchMethodException x) {
}
try{
System.out.println(method.invoke(e));
}
catch(IllegalAccessException x) {
}
catch(java.lang.reflect.InvocationTargetException x) {
}
catch(NullPointerException x) {
}
} while (! nextCommand.equals("quit"));
}

Here's a code sample that does what you want to achieve:
public class Test {
private static HashSet<Class<?>> classes = new HashSet<>();
static {
classes.add(String.class);
classes.add(Integer.class);
classes.add(GregorianCalendar.class);
}
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
X obj = new X();
obj.setField("lala");
Method method = obj.getClass().getMethod("getField", null);
System.out.println(method.invoke(obj, null));
Method setMethod = getWorkingMethod(obj);
setMethod.invoke(obj, "who let the dogs out");
System.out.println(obj.getField());
}
private static Method getWorkingMethod(Object obj) {
Method method = null;
for (Class<?> c : classes) {
try {
method = obj.getClass().getMethod("setField", c);
} catch (NoSuchMethodException | SecurityException e) {
continue;
}
if(method != null){
return method;
}
}
throw new IllegalArgumentException("No such method found!");
}
}
class X {
private String stringField;
public void setField(String s) {
stringField = s;
}
public String getField() {
return stringField;
}
}
Output:
lala
who let the dogs out
Notes:
Create a collection (I used a HashSet) that stores Class<?> objects. You will use these to iterate over the possibilities and see if a method with that argument exists.
Use a try-catch to see if the method exists (an exception is thrown when it can't find it).
This will not work for overloaded methods. If this is your scenario, you'll have to make adjustments. I expect it to be no problem though, since you said this was meant for setters (which typically don't have overloads).

You can avoid calling the getter and setter methods by directly accessing the Field through reflection.
The Field object has various get and set methods that can be used to manipulate field values.
See: http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getField%28java.lang.String%29
EXAMPLE
import java.lang.reflect.Field;
public class MyObject {
private String fieldA;
public String getFieldA() {
return fieldA;
}
public void setFieldA(String fieldA) {
this.fieldA = fieldA;
}
public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
MyObject myObject = new MyObject();
myObject.setFieldA("Test");
Class clazz = myObject.getClass();
Field field = clazz.getDeclaredField("fieldA");
field.setAccessible(true);
String fieldA = (String) field.get(myObject);
System.out.println(fieldA);
field.set(myObject, "Test2");
fieldA = (String) field.get(myObject);
System.out.println(fieldA);
field.setAccessible(false); //be sure to return field to private
}
}

Resolution (method or field resolution) in java slows down you execution time by 'orders of 10 or 100', hence not a smart design decision. So, resolve once at start time, cache method instance, and execute it from cache. Avoid frequent lookups using reflection.

Related

In Reflection, Method object's parameter class doesn't match argument class?

[This post has been edited to include a simplified copy/paste version of the underlying issue.]
I'm working on a Reflection project that will have some functionality similar to JUnit, but I'm running into an obstacle where the program seems to feel I have 2 different versions of the same class.
I've written a simple Car class as follows.
public class Car {
private String name;
public Car(String n) {
name = n;
System.out.println(name + " was constructed.");
}
public void honk() {
System.out.println("beep beep");
}
public void crash(Car other) {
System.out.println(name + " crashes into " + other.name);
}
}
I can successfully test the functionality of the car like this:
public class CarRunner {
public static void main(String[] args) {
Car a = new Car("Model T");
Car b = new Car("Tesla");
a.honk(); //prints "beep beep"
a.crash(b); //prints "Model T crashes into Tesla"
}
}
All the stuff above works fine.
Now, I want to reproduce the results of CarRuner but with some functionality testing methods I've written using reflection. Using reflection, I can request the creation of object and invoking methods with those objects. It works great until the final test, when a user-defined class is used as an argument.
import java.io.*;
import java.lang.invoke.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.net.*;
public class TesterTool {
//Where are the class files that I am testing?
private static File classPath = new File("C:\\Users\\Spatter\\Desktop\\Autograder\\SimpleCarDemo");
public static Object makeObject(String nameOfClass, Object[] arguments) {
Object retObject = null; //goal is to get an object in here of the requested class.
try {
//What type of object are we trying to construct?
URL classUrl = classPath.toURI().toURL();
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classUrl});
Class<?> c = Class.forName(nameOfClass, true, classLoader);
//What kind of arguments do we have?
Class[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentTypes[i] = arguments[i].getClass();
}
//Lets find a constructor that can accept the type of arguments we have
Constructor con = c.getConstructor(argumentTypes);
FutureTask<?> theTask = new FutureTask<Object>(new Callable<Object>()
{
public Object call() {
Object retObject = null;
try {
retObject = con.newInstance(arguments);
} catch (Exception e) { return e; }
return retObject;
}
});
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(theTask);
retObject = theTask.get(3, TimeUnit.SECONDS);
es.shutdownNow();
if (retObject instanceof Exception) throw new Exception();
} catch (Exception e) {
System.out.print("Error: Unable to construct object" + e);
}
return retObject;
}
public static Object testMethod(Object invokingObject, String methodName, Object[] arguments) {
Object retObject = null; //if the method we test returns an object, we will do the same.
try {
//What type of object are we trying to construct?
Class c = invokingObject.getClass();
//Alternate version of getting class type using ClassLoader
//Class originalc = invokingObject.getClass();
//String nameOfClass = originalc.getName();
//URL classUrl = classPath.toURI().toURL();
//URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classUrl});
//Class<?> c = Class.forName(nameOfClass, true, classLoader);
//What kind of arguments do we have?
Class[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentTypes[i] = arguments[i].getClass();
}
//Lets find a method that can accept the type of arguments we have
Method m = c.getMethod(methodName, argumentTypes);
FutureTask<?> theTask = new FutureTask<Object>(new Callable<Object>()
{
public Object call() {
Object retObject = null;
try {
retObject = m.invoke(invokingObject, arguments);
} catch (Exception e) { return e; }
return retObject;
}
});
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(theTask);
retObject = theTask.get(3, TimeUnit.SECONDS);
es.shutdownNow();
if (retObject instanceof Exception) throw new Exception();
} catch (Exception e) {
System.out.print("Error: Unable to run method " + e);
}
return retObject;
}
public static void main(String[] args) {
//Find the Car class and invoke the constructor that receives a String parameter.
Object o1 = makeObject("Car", new Object[]{"Model T"}); //this works fine.
Object o2 = makeObject("Car", new Object[]{"Tesla"}); //this works fine.
//Invoke the honk method of object o1. No parameters required.
//The result is that "beep beep" is printed.
testMethod(o1, "honk", new Object[] {}); //this works fine.
//Invoke the crash(Car c) method of o1 using o2 as the parameter.
//This should print "Model T crashes into Tesla".
testMethod(o1, "crash", new Object[] {o2}); //this doesn't work.
}
}
This last test is where my problem is coming into play. testMethod appears to be unable to find a version of the crash method that matches my request. The crash method is supposed to receive a Car object, which it does, but it doesn't seem to be good enough.
I've also tried a very complex alternate version of this where I get all the methods of the Car class and try to find one that matches the signature, but it seems to feel that an object of Class car isn't an object of Class car. (See below.)
Class objectClass = o2.getClass();
Class[] paramTypes = method.getParameterTypes(); //where method is the Method object for crash
Class paramClass = paramTypes[0]; //there was only 1 paramType. I confirmed that it's the Car class.
System.out.println(objectClass); //prints class Car
System.out.println(paramClass); //prints class Car
if (paramClass.isAssignableFrom(objectClass)) { //always returns false?
System.out.println("I want to run this method because the signature matches.");
// o1 should invoke method using FutureTask
}
isAssignableFrom() always returns false, even though they are both Car classes. Any idea what might be the problem? I've inspected the both of the Class objects (objectClass and paramClass) and they appear to be identical, even down to the paths in the ClassLoaders.
Instead of isAssignableFrom(), I've also tried isInstance, but it didn't work either:
if (paramClass.isInstance(o2)) { //also always returns false
The problem was coming from creating a new URLClassLoader object each time I was locating a Class object. By having only 1 URLClassLoader as a static variable the issue is resolved.

Java - Access to non-static properties with reflection

I want to get my class property with string name.
I have a code like this
class Test
{
public String simple = "hello";
public void getSetting()
{
try
{
Test c = new Test();
Class cls = this.getClass();
Field field = cls.getField("simple");;
}
catch(Exception e)
{
// error
}
}
}
I get an error with this code , because my property is non-static , and when i changing my property to static , it's work fine , how can i get non-static properties with reflection?
Here's a self-contained example on how to get an instance Field through reflection.
public class Main {
// the instance field
String simple = "foo";
// some static main method
public static void main(String[] args) throws Exception {
// initializing the class as we're accessing an instance method
new Main().reflect();
}
public void reflect() {
Class<?> c = this.getClass();
try {
// using getDeclaredField for package-protected / private fields
Field field = c.getDeclaredField("simple");
// printing out field's value for this instance
System.out.println(field.get(this));
}
// TODO handle better
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
catch (NoSuchFieldException n) {
n.printStackTrace();
}
}
}
Output
foo
try
{
Test c = new Test();
Class cls = c.getClass(); //Change this.getClass to c.getClass()
Field field = cls.getField("simple");;
}
The Field must be static or belong to a instance that can be get via reflection.

Getting a Method object representing the origin of an Exception

Is it possible in Java, to get a Method object representing the method in which an exception was thrown provided nothing but said exception?
Given the example:
public class Test {
protected void toFail() throws Exception {
throw new Exception();
}
protected void toFail(String someparameter) throws Exception {
throw new Exception();
}
public static void main(String[] args) {
try {
new Test().toFail("");
}catch(Exception ex){
//Clever code here
}
}
}
What clever code would allow me to get a Method object representing the toFail(String) method? Assuming its even possible :)
Clarification:
I need to uniquely identify the method that caused the exception, and i need to be able to create a reflection Method object from it. When i say uniquely i mean that i need to take the possibility of overloaded methods into account.
You can get the name of the class and method it was thrown from like this:
StackTraceElement ste = exception.getStackTrace()[0];
String className = ste.getClassName();
String methodName = ste.getMethodName();
But you can't get the Method object because the StackTraceElement does not record which of the methods with the same name.
You can get the possible Method objects (possible as with matching name) like this:
StackTraceElement ste = exception.getStackTrace()[0];
Class<?> c = Class.forName(ste.getClassName());
String mname = ste.getMethodName();
// NOTE:
// Exceptions thrown in constructors have a method name of "<init>"
// Exceptions thrown in static initialization blocks have a method name of
// "<cinit>"
if ("<init>".equals(mname)) {
// Constructors are the possible "methods", all of these:
c.getConstructors();
} else if ("<cinit>".equals(mname)) {
System.out.println("Thrown in a static initialization block!");
} else {
// Thrown from a method:
for (Method m : c.getMethods()) {
if (m.getName().equals(mname)) {
System.out.println("Possible method: " + m);
}
}
}
Use StackTrace() for this.
public class Test {
protected void toFail(String someparameter) throws Exception {
throw new Exception();
}
public static void main(String[] args) {
try {
new Test().toFail("");
}catch(Exception ex){
StackTraceElement[] stl = ex.getStackTrace();
System.out.println(stl[0].getMethodName());
}
}
}

Programmatically retrive parameters and values of a method

it's been quite a few months that i quite Java in favor of Python. Now i'm go back to java to project constraints.
now, i'm wondering if there's a way to get all the aprameters (with values) of a functions programmatically inside the function itself.
something like this
public void foo(String arg1,String arg2, Integer arg3){
... pars = ...getpars();
}
foo("abc","dfg",123);
where getpars() should return an HashMap with name,value pairs.
so from the example should be
arg1,"abc"
arg2,"dfg"
arg3,123
is there anything like this?
Unfortunately this is impossible. The only thing you can do is to retrieve the list of parameters types of a particular method using reflection.
But there is no way to get a map with name -> value of each argument passed into the method itself.
You can't get the name of the parameter, because it's no value just a name. If you wanna have the name of the parameter in your Map define a String which matches your parameter name and put it in.
Read this similar question. The accepted answer seems to have a solution for this using a third party library.
You can't get the names of the parameters dynamically, nor can you find the values in any way other than using the variable names. However, JAVA has the next best thing: variable arguments. If you want to have a dynamic number of arguments, you can declare your method as follows:
public void foo(Object... args)
When you call the method, you will call it with any number of arguments; foo(1, 2, "ABC") and foo(new File("config.dat"), new Scanner(), 88.5D) are both valid calls. Inside the function, args will be an array containing all of the parameters in order.
Just a few usage tips, though. The method declaration above is, in general, not considered good form. Usually, you can be much more specific. Think hard about whether or not you need all this flexibility, and consider using a few overloaded methods or possibly passing a HashMap to the function instead. Very rarely will you actually need to have dynamic parameters in that broad of a sense.
You could use:
void foo(String... args) {
for (String arg: args) { }
for (int i = 0; i < args.length - 1; i += 2) {
map.put(args[i], args[i + 1];
}
}
foo("a", "1", "b", "2");
Or use a Map builder, see builder-for-hashmap.
There are some hacky ways of getting the parameters values of an invoked method (But you have to understand that the parameters are unnamed, the best you can do is to get arg0.... argN).
Use Proxies
Aspect oriented programming (AspectJ, Spring AOP)
Let's consider the 1st approach. Say we want to log parameters before executing the method of some interface MethodParamsInterface, here you go. If you want to use these arguments in your logic - consider to implement them in InvocationHandler (or use EasyMock instead)
interface MethodParamsInterface {
void simpleMethod(int parm1, int parm2);
void simpleMethod(int parm1, int parm2, int param3);
}
public class MethodParams implements MethodParamsInterface {
public void simpleMethod(int parm1, int parm2) {
//business logic to be put there
}
public void simpleMethod(int parm1, int parm2, int param3) {
//business logic to be put there
}
public MethodParamsInterface wrappedInstance() throws Exception {
Class<?> proxyClass = Proxy.getProxyClass(MethodParams.class.getClassLoader(), MethodParamsInterface.class);
InvocationHandler invocationHandler = new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Map<String, Object> params = new LinkedHashMap<String, Object>(args.length);
for (int i = 0; i < args.length; i++)
params.put("arg" + i, args[i]);
//printing out the parameters:
for (Map.Entry<String, Object> paramValue : params.entrySet()) {
System.out.println(paramValue.getKey() + " : " + paramValue.getValue());
}
return MethodParams.this.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(MethodParams.this, args);
}
};
return (MethodParamsInterface) proxyClass.getConstructor(new Class[]{InvocationHandler.class}).newInstance(invocationHandler);
}
public static void main(String[] args) throws Exception {
MethodParams instance = new MethodParams();
MethodParamsInterface wrapped = instance.wrappedInstance();
System.out.println("First method call: ");
wrapped.simpleMethod(10, 20);
System.out.println("Another method call: ");
wrapped.simpleMethod(10, 20, 30);
}
}
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Test01 {
public int method01(int i, int j) {
System.out.println("Original method01");
return i + j;
}
}
class ParameterWriter {
public static <T> T getObject(T inp) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(inp.getClass());
factory.setFilter(
new MethodFilter() {
#Override
public boolean isHandled(Method method) {
return true;
}
}
);
MethodHandler handler = new MethodHandler() {
#Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
for (int i = 0; i < args.length; i++) {
System.out.println(proceed.getParameters()[i].getName() + ":" + args[i]);
}
return proceed.invoke(self, args);
}
};
return (T) factory.create(new Class<?>[0], new Object[0], handler);
}
}
public class Main {
public static void main(String[] args) {
try {
Test01 test01 = ParameterWriter.getObject(new Test01());
test01.method01(2, 3);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
output:
arg0:2
arg1:3
Original method01

How do I use getConstructor(params).newInstance(args)?

This could well be a stupid question, but I'm new to Java, so...
I've currently got some code where currently this is being used
clazz.asSubclass(asSubclassOfClass).getConstructor().newInstance()
I need to pass some arguments to the contructort so I want to change it to: clazz.asSubclass(asSubclassOfClass).getConstructor(params).newInstance(args)
What I don't understand is what I need to pass in as params and what I need to pass in as args.
Let's say I wanted to pass in a String "howdy" and some object of type XYZ called XyzObj in. How would I specify that? WHat would I pass as params and what would I pass as args?
In Java this is called Reflection.
Assuming the class has this constructor, otherwise you will get a NoSuchMethod exception I believe.
clazz.asSubclass(asSubclassOfClass)
.getConstructor(String.class,XYZ.class)
.newInstance("howdy",XyzObj);
Since you are new to Java, let me give you an easier so that you can understand what's going on under the hood when you do this.
Assume you have the following class:
public class ParentClazz{
String someVar;
public ParentClazz(){
someVar="test";
}
public ParentClazz(String someVar){
System.out.println("I have been invoked");
this.someVar=someVar;
}
}
Then you have the following main method:
public static void main(String[] args) throws ParseException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
ParentClazz.class.asSubclass(ParentClazz.class).getConstructor(String.class).newInstance("howdy");
}
If you run this you will notice the console output print message - I have been invoked. This means that using reflection you have invoked the constructor of ParentClazz.
You can do the same thing if the scenario allows you is by using standard object creation process:
ParentClazz clazz = new ParentClazz("howdy");
Hope this helps you understand it.
Here is an example of creating classes without the new keyword.
The classes take other classes both primitives and Objects as their parameters.
The example also shows the instance of a subclass and a Parent class being created
public class ConstructorInstantiateWithoutNew
{
#SuppressWarnings("rawtypes")
public static void main( String [] args )
{
Class<Drinker> clazz_drinker = Drinker.class;
Class [] paramTypes = { Fizz.class, Colour.class, int.class };
Object [] paramValues = { new Fizz(), new Colour(), new Integer(10) };
Class<Drunk> clazz_drunk = Drunk.class;
Class [] paramTypesSub = { Fizz.class, Colour.class, int.class, boolean.class };
Object [] paramValuesSub = { new Fizz(), new Colour(), new Integer(10), true };
try
{
Drinker drinker = clazz_drinker.getConstructor( paramTypes ).newInstance( paramValues );
drinker.drink();
Drunk drunk = clazz_drunk.getConstructor(paramTypesSub).newInstance(paramValuesSub);
drunk.drink();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class Drinker
{
int n;
public Drinker( Fizz f, Colour c, int n)
{
this.n = n;
}
public void drink()
{
System.out.println( "Dad drank " + (n*10) + " ml");
}
}
class Drunk extends Drinker
{
boolean trouble;
public Drunk(Fizz f, Colour c, int n, boolean inDogHouse)
{
super(f,c,n);
trouble = inDogHouse;
}
public void drink()
{
System.out.println(
"Dad is Grounded: " + trouble +
" as he drank over "+
(n*10) + " ml");
}
}
class Fizz {} class Colour {}
Hope this is useful
Kind regards
Naresh Maharaj
clazz.asSubclass(asSubclassOfClass)
.getConstructor(String.class, XYZ.class)
.newInstance("howdy", XyzObj)
Which assumes that the constructor args are in the specified order

Categories

Resources