Using java.lang.reflect.getMethod with polymorphic methods - java

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>());

Related

How to pass an unknown class Type in ResponseEntity<>

public void etisLogAround(ProceedingJoinPoint joinPoint, EtisLog etisLog) throws Throwable {
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getStaticPart().getSignature();
Method method = methodSignature.getMethod();
String[] paramNames = ((MethodSignature) joinPoint
.getSignature()).getParameterNames();
for(String paramName: paramNames) {
logger.info("paramName:" +paramName);
}
try {
Object result = joinPoint.proceed();
if(methodSignature instanceof MethodSignature) {
final Class<?>[] parameterTypes = methodSignature.getParameterTypes();
for(final Class<?> pt : parameterTypes){
logger.info("Parameter type:" + pt);
}
}
#SuppressWarnings("unchecked")
ResponseEntity<CaseOutlineHeader> returnValue = (ResponseEntity<CaseOutlineHeader>) result;
result = etisLog.trasactionDetail().toString()+" "+returnValue.getBody().getCode().toString();
} catch (IllegalArgumentException e) {
throw e;
}
}
The class CaseOutlineHeader is what I want to be changed. the parameterTypes variable contains the name of the class that I would like to pass inside the tag of the ResponseEntity<>. What if I would like to pass a different class Name. How should I do that to be flexible to accept the different class name?
If i do : ResponseEntity<parameterTypes> returnValue = (ResponseEntity<parameterTypes>) result;
it will say an error parameterTypes cannot be resolved to a type.
The problem is that your AOP method need to cast the result to something in order to get the code value it needs to log. That something must be known in advance, since you can't use type parameters in annotations, and therefore can't pass it to AOP methods. This means that all methods you access in AOP must come from a known interface, like this:
public interface LogCodeProvider {
String getLogCode();
}
public class CaseOutlineHeader implements LogCodeProvider {
#Override
public String getLogCode() {
return "My Code";
}
}
And then in your AOP method you can do like this:
#SuppressWarnings("unchecked")
ResponseEntity<LogCodeProvider> returnValue = ResponseEntity<LogCodeProvider>) result;
result = etisLog.trasactionDetail().toString()+" "+returnValue.getBody().getLogCode();
In my example I have implemented special method getLogCode() which returns a string, so each class can decide exactly what to output.
It does however look confusing to reuse the result variable to store the value returned from etisLog.trasactionDetail().
Below sample code ,
ResponseEntity<?> anyRandomMethod(){
if(any condition){
return new ResponseEntity<Animal>(new Animal(), httpstatus.OK);
}else{
return new ResponseEntity<SpaceShip>(new SpaceShip(), httpstatus.OK);
}
}

In Java, invoke method using reflection and without giving invoker object type and multiple parameters type? [duplicate]

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>());

Getting method regardless of parameters

I am trying to get method regardless of what parameters that method takes (as of now there is no method overloading and there wouldn't be in future). The only possible solution that i could come up with was
private Method getMethod(Class<?> clas, String methodName) {
try {
Method[] methods = clas.getMethods();
for (Method method : methods) {
if (method.getName().equalsIgnoreCase(methodName)) {
return method;
}
}
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
What i want to ask that is there a way to fetch a method regardless of its parameters ? I was looking at clas.getMethod ("methodName", parameters) and if i provide null in there it will try to fetch a method which has no parameters. Which wouldn't be no case.
Any ideas ?
EDIT
Thanks guys for input. In my case, i know that there would be only one method regardless of its case. The reason i am using ignoreCase is because the input will be coming from a developer (in other team) and he will be providing the name as a hard-coded string. So to keep things from spilling out of our hands, I am using a safe approach.
No. The way you've done it is the way to go. A method is identified by its signature and the signature includes the name and the parameter types.
Here is a solution that retrieves all methods with the specified class and method name regardless of the method's parameters:
public class Test
{
private class Foo
{
public void bar()
{
}
public void bar(String s)
{
}
public void goo()
{
}
}
private static Method[] getMethods(Class<?> clazz, String methodName)
{
List<Method> methods = new ArrayList<Method>();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod: declaredMethods)
{
if (declaredMethod.getName().equals(methodName))
{
methods.add(declaredMethod);
}
}
return methods.toArray(new Method[methods.size()]);
}
public static void main(String[] args)
{
Method[] methods = getMethods(Foo.class, "bar");
System.out.println(Arrays.toString(methods));
}
}
This generates the following output:
[public void com.example.Test$Foo.bar(java.lang.String), public void com.example.Test$Foo.bar()]
You've done just fine. This is basically the same as the solution to a similar problem I dealt with four years ago, creating a means to create callback methods in Java. The constructors for my Callback class were:
public Callback(Class<?> clazz, String methodName, Object parentObj) {
// Find a method with the matching name
Method[] allMethods;
try { allMethods = clazz.getMethods(); }
catch(SecurityException se) { allMethods = new Method[0]; }
int count = 0;
Method single = null;
for(Method m : allMethods) {
if(m.getName().equals(methodName)) {
single = m;
count++;
}
// Can't have more than one instance
if(count > 1)
throw new IllegalArgumentException(clazz.getName()
+ " has more than one method named " + methodName);
}
if(count == 0) // No instances found
throw new IllegalArgumentException(clazz.getName()
+ " has no method named " + methodName);
this.parentObj = parentObj;
this.method = single;
this.parameters = single.getParameterTypes();
}
public Callback(
Class<?> clazz,
String methodName,
Object parentObj,
Class<?>...parameters)
{
try { this.method = clazz.getMethod(methodName, parameters); }
catch(NoSuchMethodException nsme) { nsme.printStackTrace(); }
catch(SecurityException se) { se.printStackTrace(); }
this.parentObj = parentObj;
this.parameters = parameters;
}
My Callback class isn't really useful any more in the era of Java 8, but at the time the only real means for a "callback" in java was anonymous interface implementations, which wasn't sufficient for my use-case.
As you can see in the first constructor, it throws an exception if it finds multiple methods with the same name.
Using java streams there is a really short method of finding a method, the first match, by its name only:
Stream.of(type.getMethods())
.filter((m) -> m.getName().equals(searchedName))
.findFirst()
.get();
I think this is a short and readable possibility in this case.

JAVA: Calling Unknown Object Class Method and Passing it's Parameters

The objective is simple, I want to create a method which load a class dynamically, access its method and passing their parameters value and getting the return value at run-time.
Class which will be called
class MyClass {
public String sayHello() {
return "Hello";
}
public String sayGoodbye() {
return "Goodbye";
}
public String saySomething(String word){
return word;
}
}
Main Class
public class Main {
public void loadClass() {
try {
Class myclass = Class.forName(getClassName());
//Use reflection to list methods and invoke them
Method[] methods = myclass.getMethods();
Object object = myclass.newInstance();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().startsWith("saySome")) {
String word = "hello world";
//**TODO CALL OBJECT METHOD AND PASS ITS PARAMETER**
} else if (methods[i].getName().startsWith("say")) {
//call method
System.out.println(methods[i].invoke(object));
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private String getClassName() {
//Do appropriate stuff here to find out the classname
return "com.main.MyClass";
}
public static void main(String[] args) throws Exception {
new Main().loadClass();
}
}
My question is how to invoke method with parameters and passing its value? also getting the return value and its type.
I think you're just missing the fact that you can pass in arguments to invoke, as an Object[]:
Object result = methods[i].invoke(object, new Object[] { word });
Or using varargs, if you prefer:
Object result = methods[i].invoke(object, word);
(The above two calls are equivalent.)
See the documentation for Method.invoke for more details.
simply create the object of MyClass invoke the function like this
MyClass mc = new MyClass();
String word = "hello world";
String returnValue = mc.saySomething(word);
System.out.println(returnValue);//return hello world here
or do this
Class myclass = Class.forName(getClassName());
Method mth = myclass.getDeclaredMethod(methodName, params);
Object obj = myclass.newInstance();
String result = (String)mth.invoke(obj, args);
Try ::
Class c = Class.forName(className);
Method m = c.getDeclaredMethod(methodName, params);
Object i = c.newInstance();
String result = (String)m.invoke(i, args);

How do I pass a class as a parameter in Java?

Is there any way to pass class as a parameter in Java and fire some methods from that class?
void main()
{
callClass(that.class)
}
void callClass(???? classObject)
{
classObject.somefunction
// or
new classObject()
//something like that ?
}
I am using Google Web Toolkit and it does not support reflection.
public void foo(Class c){
try {
Object ob = c.newInstance();
} catch (InstantiationException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
Here are some good examples on Reflection API
How to invoke method using reflection
import java.lang.reflect.*;
public class method2 {
public int add(int a, int b)
{
return a + b;
}
public static void main(String args[])
{
try {
Class cls = Class.forName("method2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod(
"add", partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj
= meth.invoke(methobj, arglist);
Integer retval = (Integer)retobj;
System.out.println(retval.intValue());
}
catch (Throwable e) {
System.err.println(e);
}
}
}
Also See
Java Reflection
public void callingMethod(Class neededClass) {
//Cast the class to the class you need
//and call your method in the class
((ClassBeingCalled)neededClass).methodOfClass();
}
To call the method, you call it this way:
callingMethod(ClassBeingCalled.class);
Construct your method to accept it-
public <T> void printClassNameAndCreateList(Class<T> className){
//example access 1
System.out.print(className.getName());
//example access 2
ArrayList<T> list = new ArrayList<T>();
//note that if you create a list this way, you will have to cast input
list.add((T)nameOfObject);
}
Call the method-
printClassNameAndCreateList(SomeClass.class);
You can also restrict the type of class, for example, this is one of the methods from a library I made-
protected Class postExceptionActivityIn;
protected <T extends PostExceptionActivity> void setPostExceptionActivityIn(Class <T> postExceptionActivityIn) {
this.postExceptionActivityIn = postExceptionActivityIn;
}
For more information, search Reflection and Generics.
Use
void callClass(Class classObject)
{
//do something with class
}
A Class is also a Java object, so you can refer to it by using its type.
Read more about it from official documentation.
This kind of thing is not easy. Here is a method that calls a static method:
public static Object callStaticMethod(
// class that contains the static method
final Class<?> clazz,
// method name
final String methodName,
// optional method parameters
final Object... parameters) throws Exception{
for(final Method method : clazz.getMethods()){
if(method.getName().equals(methodName)){
final Class<?>[] paramTypes = method.getParameterTypes();
if(parameters.length != paramTypes.length){
continue;
}
boolean compatible = true;
for(int i = 0; i < paramTypes.length; i++){
final Class<?> paramType = paramTypes[i];
final Object param = parameters[i];
if(param != null && !paramType.isInstance(param)){
compatible = false;
break;
}
}
if(compatible){
return method.invoke(/* static invocation */null,
parameters);
}
}
}
throw new NoSuchMethodException(methodName);
}
Update:
Wait, I just saw the gwt tag on the question. You can't use reflection in GWT
Adding <T> T as return type worked for me. Ex with json deserialize
public static <T> T fromJson(String json, Class<T> classOfT){
return gson().fromJson(json, classOfT);
}
I am not sure what you are trying to accomplish, but you may want to consider that passing a class may not be what you really need to be doing. In many cases, dealing with Class like this is easily encapsulated within a factory pattern of some type and the use of that is done through an interface. here's one of dozens of articles on that pattern: http://today.java.net/pub/a/today/2005/03/09/factory.html
using a class within a factory can be accomplished in a variety of ways, most notably by having a config file that contains the name of the class that implements the required interface. Then the factory can find that class from within the class path and construct it as an object of the specified interface.
As you said GWT does not support reflection. You should use deferred binding instead of reflection, or third party library such as gwt-ent for reflection suppport at gwt layer.
Se these:
http://download.oracle.com/javase/tutorial/extra/generics/methods.html
here is the explaniation for the template methods.
Have a look at the reflection tutorial and reflection API of Java:
https://community.oracle.com/docs/DOC-983192enter link description here
and
http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html
Class as paramater. Example.
Three classes:
class TestCar {
private int UnlockCode = 111;
protected boolean hasAirCondition = true;
String brand = "Ford";
public String licensePlate = "Arizona 111";
}
--
class Terminal {
public void hackCar(TestCar car) {
System.out.println(car.hasAirCondition);
System.out.println(car.licensePlate);
System.out.println(car.brand);
}
}
--
class Story {
public static void main(String args[]) {
TestCar testCar = new TestCar();
Terminal terminal = new Terminal();
terminal.hackCar(testCar);
}
}
In class Terminal method hackCar() take class TestCar as parameter.

Categories