Reputation: 142
First, read the code below.
I want to call the invoke method of the SuperClass, without having to cast the return value to the corresponding type. The problem is that the SuperClass implementation is located one project and the SuperClassImpl1 and 2, are in another project.
In runtime these are in different classloaders, i.e. ClassLoader 1 knows about SuperClass but not about SuperclassImpl1. I have a "SuperClassLoader" which gives ClassLoader 1 a class object of e.g. SuperClassImpl2, but I can only use it as a SuperClass object.
Right now my code looks like the one below, and i have to cast it to e.g. a Map if i know that the method name I give to invoke returns a Map.
What I would want is that depending on the return type of the method given by the methodName, the return type of the invoke method should change accordingly.
E.g. invoke("getMyMap")
should return a Map. Note, I dont want this:
Map m = invoke("getMyMap");
m.get(...);
I want this:
invoke("getMyMap").get(...);
Is it possible?
public abstract class SuperClass(){
public Object invoke(String methodName){
Method method = this.getClass().getDeclaredMethod(methodName);
return method.invoke(this);
}
}
public class SuperClassImpl1() extends SuperClass{
Map<String,String> myMap;
public SuperClassImpl1(){
myMap = new HashMap<String,String>();
}
public Map<String,String> getMyMap(){
return myMap;
}
}
public class SuperClassImpl2() extends SuperClass{
List<String> myList;
public SuperClassImpl2(){
myList = new ArrayList<String>();
}
public ArrayList<String> getMyList(){
return myList;
}
}
public class MainClass(){
public static void main(String[] args){
SuperClass mySuperClass1 = new SuperClassImpl1();
SuperClass mySuperClass2 = new SuperClassImpl2();
System.out.println("Something in my map: "+mySuperClass1.invoke("getMyMap").get("Some value"));
// something similar using mySuperClass2 here...
}
}
Upvotes: 1
Views: 4542
Reputation: 219
The problem is that, in the definition of invoke(), the return type is always an object. This is just the way things are.(documentation)
If you want to use reflection, you will have to use casting at some point. You could create a wrapper method that does the casting for you, but there is no getting around it.
The way I got it to work was like this:
System.out.println("Something in my map: "+((Map<?, ?>) mySuperClass1.invoke("getMyMap")).get("Some value"));
Upvotes: 2
Reputation: 8488
You can't do it because generic types are checked at compile time, and the actual return type of the methods you invoke is not known until run time.
Map m = invoke("getMyMap"); // checked at compile time
return method.invoke(this); // resolved at run time
Upvotes: 0
Reputation: 7423
The return type of the method can not depend on the String that you pass to it, because the type is resolved at compile time while the argument is only known at run time.
You can, however add parameter to your classes.
public abstract class SuperClass<T> {
public T invoke(String methodName){
Method method = this.getClass().getDeclaredMethod(methodName);
return (T) method.invoke(this);
}
}
public class SuperClassImpl1() extends SuperClass<Map<String, String>> {
...
}
public class MainClass(){
public static void main(String[] args){
SuperClass<Map<String, String>> mySuperClass1 = new SuperClassImpl1();
SuperClass<ArrayList<String>> mySuperClass2 = new SuperClassImpl2();
System.out.println("Something in my map: "+mySuperClass1.invoke("getMyMap").get("Some value"));
// something similar using mySuperClass2 here...
}
}
Upvotes: 0