Combining Java generics and reflection to avoid casting
I have some code like this:
Object doMethod(Method m, Object... args) throws Exception {
Object obj = m.getDeclaringClass().getConstructor().newInstance();
return m.invoke(obj, args);
}
The code I use is a little more complex, but that's the idea of it. To invoke doMethod
I do something like this:
Method m = MyClass.class.getMethod("myMethod", String.class);
String result = (String)doMethod(m, "Hello");
This works just fine for me (variable number of arguments and all). The thing that irks me is the necessary cast to String
in the caller. Since myMethod
declares that it returns a String
, I'd like doMethod
to be smart enough to change its return type to also be String
. Is th开发者_如何学Cere some way of using Java generics to accomplish something like this?
String result = doMethod(m, "Hello");
int result2 = doMethod(m2, "other", "args");
Sure,
@SuppressWarnings("unchecked")
<T> T doMethod(Method m, Class<T> returnType, Object Args ...) {
Object obj = m.getDeclaringClass().getConstructor().newInstance();
return (T) m.invoke(obj, args);
}
String result = doMethod(m, m.getReturnType(), "Hello");
One is idly curious about the architecture that calls for such a thing to be done, but that's well out of scope :)
If you don't like that you can also leave off the binding of returnType and the compiler will automatically cast it to whatever you're assigning the return type to. e.g., this is legal:
@SuppressWarnings("unchecked")
<T> T doMethod(Method m, Object Args ...) {
Object obj = m.getDeclaringClass().getConstructor().newInstance();
return (T) m.invoke(obj, args);
}
The cast will be to whatever you are attempting to assign it to, but I think most people would consider it suspect.
I wish that Method had been parameterized to capture the return type. You could always do that yourself by wrapping Method with your own MethodEx... Doing so would allow you to provide some pretty nice facades too...
public class MethodEx<T> {
private final Method _method;
private final Class<T> _returnType;
public MethodEx(Method method, Class<T> returnType) {
_method = method;
_returnType = returnType;
}
public T invoke(Object object, Object... args) throws InvocationTargetException {
try {
return _returnType.cast(_method.invoke(object, args));
}
// good opportunity to hide/wrap other exceptions if your
// usecases don't really encounter them
}
}
This is just a starting point -- you can have factory methods on MethodEx that does a lot of up-front validation to make sure method is public, etc.
Finally, if you are caching the Method instances etc and dealing with dynamically loaded classes, this would also be a good opportunity to defensively introduce weak references (to the method and the return type) so you don't have to be as careful about pegging entire classloaders throughout your code.
You could try
public <T> T doMethod(Method m, Class<T> clazz, Object... args);
Although it moves the cast to the routine itself. In general what you're trying to do doesn't sound like good practice. Reflection itself incurs some performance overhead, but perhaps that not of concern?
精彩评论