description:
I need use getMethod, it requires the parameterTypes.
The origin method requires double (a primitive type, not Double), and I can't change origin method.
I can't just input double.class in parameterTypes, because the s maybe diffierent types, such as Integer(not int).
The method parameter in Foo.java are always and only primitive types.
code:
test.java
public static void main( String args[] )
{
Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
Method method = obj.getClass().getMethod("return" + type, s.getClass());// got NoSuchMethodException here, because it requires `double` not Double
System.out.println(method.invoke(obj,s));
} catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e) {
e.printStackTrace();
}
}
}
Foo.java //(I can't change/add code/delete in this part)
public class Foo {
public double returnDouble(double type){
return type;
}
public int returnInt(int type){
return type;
}
}
what I have tried:
Use Map
public static void main( String args[] )
{
Object obj = new Foo();
// Object s = 1;
// String type = "Int";
Object s = 1.2;
String type = "Double";
Map<String, Class> methodClassMap = new HashMap<String, Class>() {{
put("Double",double.class);
put("Integer",int.class);
}};
try {
Method method = obj.getClass().getMethod("return" + type, methodClassMap.get(s.getClass().getSimpleName()));
System.out.println(method.invoke(obj,s));
} catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e) {
e.printStackTrace();
}
}
}
It worked, but I have to list all possible type of value the s.
question:
Any better solution than using Map? Maybe use generic?
When you know beforehand that the target method always uses a primitive types, you can use the unwrap() method of MethodType of the java.lang.invoke package.
Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
MethodType mt = MethodType.methodType(s.getClass(), s.getClass()).unwrap();
Method method = obj.getClass().getMethod("return" + type, mt.parameterArray());
System.out.println(method.invoke(obj, s));
} catch(ReflectiveOperationException e) {
e.printStackTrace();
}
Alternatively, when you’re already using the method type of the java.lang.invoke package, you can also use a method handle to perform the invocation.
Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
MethodType mt = MethodType.methodType(s.getClass(), s.getClass()).unwrap();
MethodHandle mh = MethodHandles.lookup().bind(obj, "return" + type, mt);
System.out.println(mh.invoke(s));
} catch(Throwable e) {
e.printStackTrace();
}
But note that unlike Reflection, the return type has to be correctly specified for the lookup. I’m assuming the same return type as the parameter type, like in your example.
Related
I need to grab a int from a object[][], but have no idea how to do it with reflection.
I used this method to grab it from an object[]
public static Object getInterfaceObject(String clazz, String field, Object obj, int index) {
try {
Client client = Boot.client;
ClassLoader cl = client.classLoader;
Class<?> c = cl.loadClass(clazz);
Field f = c.getDeclaredField(field);
f.setAccessible(true);
Object arr = f.get(client.getClient());
return (Object) Array.get(arr, index);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Since this next one is object[][], I don't know how to go about it.
I want to basically be able to do
getInterfaceObject()[arg1][arg2].otherStuff();
You can downcast the Object to Object[][] like this:
((Object[][]) getInterfaceObject())[arg1][arg2].otherStuff();
Or do that inside getInterfaceObject:
public static Object[][] getInterfaceObject(String clazz, String field, Object obj, int index) {
try {
Client client = Boot.client;
ClassLoader cl = client.classLoader;
Class<?> c = cl.loadClass(clazz);
Field f = c.getDeclaredField(field);
f.setAccessible(true);
Object arr = f.get(client.getClient());
return (Object[][]) Array.get(arr, index);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
And leave your callsite as you desired:
getInterfaceObject()[arg1][arg2].otherStuff();
A few of my opinions on clean code (take or leave of course):
Prefer throw new RuntimeException(e); to e.printStackTrace(); (alter the IDE template)
Prefer explicit code to reflection. Reflection loses type safety.
Prefer specific types to Object
Consider the following snippet:
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test = new ReflectionTest();
String object = new String("Hello!");
// 1. String is accepted as an Object
test.print(object);
// 2. The appropriate method is not found with String.class
try {
java.lang.reflect.Method print
= test.getClass().getMethod("print", object.getClass());
print.invoke(test, object);
} catch (Exception ex) {
ex.printStackTrace(); // NoSuchMethodException!
}
}
public void print(Object object) {
System.out.println(object.toString());
}
}
getMethod() is obviously unaware that a String could be fed to a method that expects an Object (indeed, it's documentation says that it looks for method with the specified name and exactly the same formal parameter types).
Is there a straightforward way to find methods reflectively, like getMethod() does, but taking polymorphism into account, so that the above reflection example could find the print(Object) method when queried with ("print", String.class) parameters?
The reflection tutorial
suggest the use of Class.isAssignableFrom() sample for finding print(String)
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.startsWith("print") {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if ((pType.length != 1)
|| !String.class.isAssignableFrom(pType[0].getClass())) {
continue;
}
}
The easy way to do this is via java.beans.Statement or java.beans.Expression. Does all these hard yards for you.
getMethod() is obviously unaware that
a String could be fed to a method
that expects an Object
'Unaware' is a strange way to put it. getMethod() adheres to its specification. You have to supply the formal parameters, not the types of the actual arguments.
FYI, it is how I invoke method using reflection with multiple parameters without giving their types.
public class MyMethodUtils {
/**
* Need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters, Class[] parameterClasses) throws Exception {
Method method = invoker.getClass().getMethod(methodName, parameterClasses);
Object returnValue = method.invoke(invoker, parameters);
return returnValue;
}
/**
* No need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters) throws Exception {
Method[] allMethods = invoker.getClass().getDeclaredMethods();
Object returnValue = null;
boolean isFound = false;
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.equals(methodName)) {
continue;
}
Class[] methodParaClasses = m.getParameterTypes();
for (int i = 0; i < methodParaClasses.length; i++) {
Class<?> parameterClass = parameters[i].getClass();
Class<?> methodParaClass = methodParaClasses[i];
boolean isAssignable = methodParaClass.isAssignableFrom(parameterClass);
if (!isAssignable) {
continue;
}
}
returnValue = m.invoke(invoker, parameters);
isFound = true;
}
if (!isFound) {
throw new RuntimeException("Cannot find such method");
}
return returnValue;
}
}
Sample Usage:
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks }, new Class[] { String.class, Collection.class });
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks });
However, for the method invoke(Object invoker, String methodName, Object[] parameters), it is possible to invoke wrong method if the signature is ambiguous. For example, if there is two methods for the invoker:
public void setNameAndMarks(String name, Collection<Integer> marks);
public void setNameAndMarks(String name, ArrayList<Integer> marks);
Passing the following parameter may invoke wrong method
setNameAndMarks("John", new ArrayList<Integer>());
I am making a class object using the a java bean in my code. Then I am calling a particular method of that class obj
public static void runUnitTest(String className, String methodName, List<Object> inputParams, Object expectedReturnValue){
try {
// Make the class object to be tested on
Object classObj = Class.forName(className).newInstance();
Method calledMethod = classObj.getClass().getMethod(methodName,inputParams.get(0).getClass());
Object returnVal = calledMethod.invoke(classObj,inputParams.get(0));
}catch (InstantiationException | IllegalAccessException
| ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
I call it this way :
public static void main(String[] args) throws IOException {
List<Object> inputParams = new ArrayList<Object>();
inputParams.add(new BigDecimal(1234));
runUnitTest("NumberTest","getOutputNumber",inputParams,new BigDecimal(5678));
}
The code of NumberTest:
public class NumberTest{
public BigDecimal getOutputNumber(BigDecimal numberId) {
if(numberId.intValue() == 1234)
{
return new BigDecimal(5678);
}else
return new BigDecimal(0);
}
public BigDecimal getAdditionalOutputNumber(BigDecimal numberId, String additionalInfo) {
if(numberId.intValue() == 1234 && "Pass".equals(additionalInfo))
{
return new BigDecimal(5678);
}else
return new BigDecimal(0);
}
}
This works fine as I know that the method getOutputNumber has only one parameter. But when I have to call the same code for multiple methods where number of parameters differ (e.g. getAdditionalOutputNumber) I can't use the same code. I don't want to use a multiple if else or case block on the basis of size of inputParams.
Is there a generic way of calling the below :
Method calledMethod = classObj.getClass().getMethod(methodName,**?? What to pass here ??**);
Object returnVal = calledMethod.invoke(classObj,**?? What to pass here ??**);
You just have to build suitable arrays from the list of parameters to call the reflection API.
Class[] types = new Class[inputParams.size()];
int i = 0;
for(Object param:inputParams) {
types[i++] = param.getClass();
}
Method calledMethod = classObj.getClass().getMethod(methodName,types);
Object returnVal = calledMethod.invoke(classObj,inputParams.toArray());
There might be some issues with primitive types and null values.
I've a list of strings, field names, of a class in a loop from resource bundle. I create an object and then using loop i want to set values for that object. For example, for object
Foo f = new Foo();
with parameter param1, I have string "param1" and I somehow want to concate "set" with it like "set"+"param1" and then apply it on f instance as:
f.setparam1("value");
and same for getter. I know reflection will help but I couldn't manage to do it.
Please help. Thanks!
You can do something like this. You can make this code more generic so that you can use it for looping on fields:
Class aClass = f.getClass();
Class[] paramTypes = new Class[1];
paramTypes[0] = String.class; // get the actual param type
String methodName = "set" + fieldName; // fieldName String
Method m = null;
try {
m = aClass.getMethod(methodName, paramTypes);
} catch (NoSuchMethodException nsme) {
nsme.printStackTrace();
}
try {
String result = (String) m.invoke(f, fieldValue); // field value
System.out.println(result);
} catch (IllegalAccessException iae) {
iae.printStackTrace();
} catch (InvocationTargetException ite) {
ite.printStackTrace();
}
Apache Commons BeanUtils does it.
Consider the following snippet:
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test = new ReflectionTest();
String object = new String("Hello!");
// 1. String is accepted as an Object
test.print(object);
// 2. The appropriate method is not found with String.class
try {
java.lang.reflect.Method print
= test.getClass().getMethod("print", object.getClass());
print.invoke(test, object);
} catch (Exception ex) {
ex.printStackTrace(); // NoSuchMethodException!
}
}
public void print(Object object) {
System.out.println(object.toString());
}
}
getMethod() is obviously unaware that a String could be fed to a method that expects an Object (indeed, it's documentation says that it looks for method with the specified name and exactly the same formal parameter types).
Is there a straightforward way to find methods reflectively, like getMethod() does, but taking polymorphism into account, so that the above reflection example could find the print(Object) method when queried with ("print", String.class) parameters?
The reflection tutorial
suggest the use of Class.isAssignableFrom() sample for finding print(String)
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.startsWith("print") {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if ((pType.length != 1)
|| !String.class.isAssignableFrom(pType[0].getClass())) {
continue;
}
}
The easy way to do this is via java.beans.Statement or java.beans.Expression. Does all these hard yards for you.
getMethod() is obviously unaware that
a String could be fed to a method
that expects an Object
'Unaware' is a strange way to put it. getMethod() adheres to its specification. You have to supply the formal parameters, not the types of the actual arguments.
FYI, it is how I invoke method using reflection with multiple parameters without giving their types.
public class MyMethodUtils {
/**
* Need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters, Class[] parameterClasses) throws Exception {
Method method = invoker.getClass().getMethod(methodName, parameterClasses);
Object returnValue = method.invoke(invoker, parameters);
return returnValue;
}
/**
* No need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters) throws Exception {
Method[] allMethods = invoker.getClass().getDeclaredMethods();
Object returnValue = null;
boolean isFound = false;
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.equals(methodName)) {
continue;
}
Class[] methodParaClasses = m.getParameterTypes();
for (int i = 0; i < methodParaClasses.length; i++) {
Class<?> parameterClass = parameters[i].getClass();
Class<?> methodParaClass = methodParaClasses[i];
boolean isAssignable = methodParaClass.isAssignableFrom(parameterClass);
if (!isAssignable) {
continue;
}
}
returnValue = m.invoke(invoker, parameters);
isFound = true;
}
if (!isFound) {
throw new RuntimeException("Cannot find such method");
}
return returnValue;
}
}
Sample Usage:
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks }, new Class[] { String.class, Collection.class });
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks });
However, for the method invoke(Object invoker, String methodName, Object[] parameters), it is possible to invoke wrong method if the signature is ambiguous. For example, if there is two methods for the invoker:
public void setNameAndMarks(String name, Collection<Integer> marks);
public void setNameAndMarks(String name, ArrayList<Integer> marks);
Passing the following parameter may invoke wrong method
setNameAndMarks("John", new ArrayList<Integer>());