Combining Java generics and reflection to avoid casting - java

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 there 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?

Related

How can I use generics to provide a "universal getter method"?

This question is more theoretical (what I want to do is more complicated but this is the part I'm stuck on), so apologies for the contrived example which may not make much sense.
Say I have some class that has methods that return its value in different forms:
public class MyObject {
public String getAsString() {...}
public int getAsInt() {...}
// and so on
}
I'm trying to create a single method to allow me to specify which MyObject method to call via its parameters. Something like:
public <T> T getValue(MyObject obj, Class<T> c) {
if (c == String.class) {
return obj.getAsString();
} else if (c == Integer.class) {
return obj.getAsInt();
} // and so on
}
So then I would like to call this method like this, assuming obj is a MyObject:
String s = getValue(obj, String.class);
int i = getValue(obj, Integer.class);
// and so on
I'm getting the compile error "Type mismatch: cannot convert from String to T" (and likewise for Integer) in the getValue method. Clearly I'm just not understanding generics fully, but I thought this was the general idea behind generics - here I'm specifying (or trying to specify, at least) the real type of T via the parameter c. What am I doing wrong?
If you want to to create a single method with really safe casts - then I would suggest to setup a mapping between the expected type and its respective getter.
Given the MyObject class definition as:
public class MyObject {
public int getIntValue() {
return 42;
}
public String getStringValue() {
return "Answer";
}
}
So that the "accessor" class could look as follows (it can be generalized further if needed):
public class MyObjectAccessor {
private final Map<Class<?>, Function<MyObject, ?>> registry = new HashMap<>();
public Accessor() {
registerGetter(Integer.class, MyObject::getIntValue);
registerGetter(String.class, MyObject::getStringValue);
}
private <T> void registerGetter(Class<T> type, Function<MyObject, T> getter) {
registry.put(type, getter);
}
#SuppressWarnings("unchecked")
public <T> Optional<T> getValue(MyObject obj, Class<T> type) {
return (Optional<T>) ofNullable(registry.get(type)).map(getter -> getter.apply(obj));
}
}
This would allow you to make the behavior much more predictable with a control over the unexpected/missing mapping.
(Here it returns an Optional back, but you can also throw an exception or provide a default value or do something else)
Please note that the cast inside getValue is actually a safe checked cast (even though it was marked with #SuppressWarnings) as the "safety" proof here is a little bit beyond current javac's capability of static code analysys.
First of all, if getAsString and getAsInt are not doing any conversion (such as would be the case if all your values were stored as strings), you probably can reduce your method to this:
public <T> T getValue(MyObject obj) {
return (T) obj.value;
}
This will have an unchecked cast warning, but that's not worse than leaving the typing decision to your caller (so I'd just #SuppressWarnings("unchecked") it). If your caller uses the wrong target type, they will get a ClassCastException at runtime, which I assume goes well with your current contract. But you can keep c.cast(obj.getAsX()) if you want the exception to be raised in your own method.
With the above, your callers would just use:
String s = getValue(obj);
int i = getValue(obj);
If, however, you are actually converting data in getAs... methods, then you will need to cast in your generic getter after dispatching to the correct getAsX method, at least as ProGu suggested (i.e., return c.cast(obj.getAsX()) in each branch).

Different return type for the same method

i need to implement the newInstance method in order to make this code to work:
protected IDatabase createDatabase() throws Exception{
return newInstance(CLASS_NAME_DATABASE);
}
protected IDataLoader createDataLoader() throws Exception{
return newInstance(CLASS_NAME_DATA_LOADER);
}
I'm not allowed to touch this code, but when i try to implement the newInstance as Object it means i need to cast the newInstance in the code above you. Is there a way for me to implement newInstance without changing the code?
Thanks!
If the parameter to newInstance were a Class object, or something else with a generic type parameter, you could do the following, which would avoid needing to cast the return value, and be type-safe:
protected <T> T newInstance(Class<T> klass) throws Exception {
return klass.newInstance();
}
(Note: klass.newInstance() is the reflection API method, not the same as the method being defined, despite the same name.)
However, you can use generics no matter what sort of parameters newInstance takes:
#SuppressWarnings("unchecked")
protected <T> T newInstance(String className) throws Exception {
return (T)Class.forName(className).newInstance();
}
This is a bit dirty, because it completely eliminates compile-time error checking on the type of the return value. The compiler will allow IDataLoader x = newInstance(CLASS_NAME_DATA_LOADER); but will just as happily allow int x = newInstance(CLASS_NAME_DATA_LOADER); (which actually casts it to an Integer then unboxes it).
However, manually casting an Object return value would also not be checked at compile time, so using generics here is probably still an improvement since it reduces the number of times you have to write each type.

How to find an overloaded method in Java?

When writing something like
doit(43, 44, "hello");
the compiler knows which overloaded method is to be called. When I want to do the same via reflection, I need to find out myself, that the method is
doit(Integer, double, CharSequence...);
and obtain it via something like
Class[] types = {Integer.class, double.class, CharSequence[].class};
declaringClass.getDeclaredMethod("doit", types);
I wonder if there's already something allowing me to write just
Method m = getMethod(declaringClass, "doit", 43, 44, "hello");
I wonder if somebody did this already, as the JLS is a bit complicated in this respect.
Actually, behaving exactly like the compiler is impossible as in Phase 1 the compiler accepts only methods matching without boxing and unboxing. When calling my hypothetical getMethod from above, the distinction between primitives and their wrappers is already lost (because of autoboxing when passing arguments via varargs). This problem seems to have no solution, so let's ignore it.
As suggested in an answer, BeanUtils.invokeMethod comes close. It's supposed to find the best match, whatever it means. Looking at MethodUtils.getMatchingAccessibleMethod shows that
it knows nothing about varargs
it's non-deterministic
so I'm looking for something better.
Alternatively you could use Bean Utils from Apache Commons:
public static Method getAccessibleMethod(
Class clazz,
String methodName,
Class[] parameterTypes)
According documentation:
Return an accessible method (that is, one that can be invoked via
reflection) with given name and parameters. If no such method can be
found, return null. This is just a convenient wrapper for
getAccessibleMethod(Method method).
Parameters:
clazz - get method from this class
methodName - get method with this name
parameterTypes - with these parameters types
The implementation get the accessible method and goes up in the hierarchy until it founds a match to it.
Direct to the Invocation
In order to perform invocation directly as you asked, you could use this method from the same API:
public static Object invokeExactMethod(
Object object,
String methodName,
Object[] args,
Class[] parameterTypes)
throws
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException
or even
public static Object invokeExactMethod(
Object object,
String methodName,
Object[] args)
throws
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException
that first locates the method using getAccessibleMethod and later on invokes it.
The MethodHandle is a new way to get a overloaded method using a signature (java 7):
Example:
static class A {
public String get() {
return "A";
}
}
static class B extends A {
public String get() {
return "B";
}
}
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(String.class);
MethodHandle mh = lookup.findVirtual(A.class, "get", mt);;
System.out.println(mh.invoke(new B()));
}
Outputs:
B

Problem at JUnit test with generics

In my utility method:
public static <T> T getField(Object obj, Class c, String fieldName) {
try {
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(obj);
} catch (Exception e) {
e.printStackTrace();
fail();
return null;
}
}
The line
return (T) field.get(obj);
gives the warning "Type safety: Unchecked cast from Object to T";
but I cannot perform instanceof check against type parameter T,
so what am I suppose to do here?
The annotation #SuppressWarnings will stop the compiler reporting this warning. I don't think there's any way you can get away from the compiler warning when using reflection like this. Something like the following:
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
#SuppressWarnings(value="unchecked")
T t = (T) field.get(obj);
return t;
You can easily solve this problem by adding an additional parameter to your method which will specify the type of the filed, the method will then look as follows:
public static <T> T getField(Class<T> fieldType, Object obj, Class<?> c,
String fieldName)
{
try {
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
Object value = field.get(obj);
return fieldType.cast(value);
} catch (Exception e) {
e.printStackTrace();
fail();
return null;
}
}
And here's how you can use it: getField(String.class, new G(), G.class, "s") where G is defined as:
public class G {
String s = "abc";
}
A 2nd improvement is to eliminate the c parameter of getFiled(). c can be obtained inside the method by invoking obj.getClass(). The only caveat is that this will give you the dynamic type of the object so you mat want to loop over all of C's superclasses until you find the field you're looking for, or until you arrive at Object (You will also need to use c.getFields() and look for the field in the resulting array).
I think that these changes will make your method easier to use and less prone to errors so it's worth the effort.
Generics are there to provide type safety in places where you didn't previously have any in Java. So it used to be that if you had a list full of Strings you had to do:
String myString = (String)myList.get(0);
but now you can retrieve it without casting it:
String myString = myList.get(0); //Compiler won't complain
When you generify using the variable T, you are saying T is a placeholder for a specific type, which will be defined on the instance of the class at instantiation time. For instance:
public class ArrayList<T> {
public ArrayList<T> {
....
}
}
allows you to instantiate the list with:
ArrayList<String> myList = new ArrayList<String>();
Now every function on ArrayList will return a String, and the compiler knows this so it doesn't require a cast. Each of those functions was defined much like yours above:
public T get(int index);
public void set(int index, T object);
at compile time they become:
public String get(int index);
public void set(int index, String object);
In your case, however, you seem to be trying to use T as a wildcard, which is different from a placeholder for a specific type. You might call this method three times for three different fields, each of which has a different return type, right? This means that, when you instantiate this class, you cannot pick a single type for T.
In general, look at your method signatures and ask yourself "will a single type be substituted for T for each instance of this class"?
public static <T> T getField(Object obj, Class c, String fieldName)
If the answer is "no", that means this is not a good fit for Generics. Since each call will return a different type, you have to cast the results from the call. If you cast it inside this function, you're losing any benefits Generics would provide, and might as well save yourself the headaches.
If I've misunderstood your design, and T does refer to a single type, then simply annotating the call with #SuppressWarnings(value="unchecked") will do the trick. But if I've understood correctly, fixing this error will just lead you to a long road of confusion unless you grok what I've written above.
Good luck!
As suggested above, you can specify the expected type of the field and call the cast method.
Also. you don't need to pass argument object's class. You can find out what it is by calling obj.getClass()
This simplifies your code to
public static <T> T getField(Object obj, Class<T> fieldClass, String fieldName) {
try {
Class<?> declaringClass = obj.getClass();
Field field = declaringClass.getDeclaredField(fieldName);
field.setAccessible(true);
return fieldClass.cast(field.get(obj));
}
catch (Exception e) {
throw new AssertionFailedError();
}
}

Type-safe method reflection in Java

Is any practical way to reference a method on a class in a type-safe manner? A basic example is if I wanted to create something like the following utility function:
public Result validateField(Object data, String fieldName,
ValidationOptions options) { ... }
In order to call it, I would have to do:
validateField(data, "phoneNumber", options);
Which forces me to either use a magic string, or declare a constant somewhere with that string.
I'm pretty sure there's no way to get around that with the stock Java language, but is there some kind of (production grade) pre-compiler or alternative compiler that may offer a work around? (similar to how AspectJ extends the Java language) It would be nice to do something like the following instead:
public Result validateField(Object data, Method method,
ValidationOptions options) { ... }
And call it with:
validateField(data, Person.phoneNumber.getter, options);
As others mention, there is no real way to do this... and I've not seen a precompiler that supports it. The syntax would be interesting, to say the least. Even in your example, it could only cover a small subset of the potential reflective possibilities that a user might want to do since it won't handle non-standard accessors or methods that take arguments, etc..
Even if it's impossible to check at compile time, if you want bad code to fail as soon as possible then one approach is to resolve referenced Method objects at class initialization time.
Imagine you have a utility method for looking up Method objects that maybe throws error or runtime exception:
public static Method lookupMethod( Class c, String name, Class... args ) {
// do the lookup or throw an unchecked exception of some kind with a really
// good error message
}
Then in your classes, have constants to preresolve the methods you will use:
public class MyClass {
private static final Method GET_PHONE_NUM = MyUtils.lookupMethod( PhoneNumber.class, "getPhoneNumber" );
....
public void someMethod() {
validateField(data, GET_PHONE_NUM, options);
}
}
At least then it will fail as soon as MyClass is loaded the first time.
I use reflection a lot, especially bean property reflection and I've just gotten used to late exceptions at runtime. But that style of bean code tends to error late for all kinds of other reasons, being very dynamic and all. For something in between, the above would help.
There isn't anything in the language yet - but part of the closures proposal for Java 7 includes method literals, I believe.
I don't have any suggestions beyond that, I'm afraid.
Check out https://proxetta.jodd.org/refs/methref. It uses the Jodd proxy library (Proxetta) to proxy your type. Not sure about its performance characteristics, but it does provide type safety.
An example: Suppose Str.class has method .boo(), and you want to get its name as the string "boo":
String methodName = Methref.of(Str.class).name(Str::boo);
There's more to the API than the example above: https://oblac.github.io/jodd-site/javadoc/jodd/methref/Methref.html
Is any practical way to reference a method on a class in a type-safe manner?
First of all, reflection is type-safe. It is just that it is dynamically typed, not statically typed.
So, assuming that you want a statically typed equivalent of reflection, the theoretical answer is that it is impossible. Consider this:
Method m;
if (arbitraryFunction(obj)) {
m = obj.getClass().getDeclaredMethod("foo", ...);
} else {
m = obj.getClass().getDeclaredMethod("bar", ...);
}
Can we do this so that that runtime type exceptions cannot happen? In general NO, since this would entail proving that arbitraryFunction(obj) terminates. (This is equivalent to the Halting Problem, which is proven to be unsolvable in general, and is intractable using state-of-the-art theorem proving technology ... AFAIK.)
And I think that this road-block would apply to any approach where you could inject arbitrary Java code into the logic that is used to reflectively select a method from an object's class.
To my mind, the only moderately practical approach at the moment would be to replace the reflective code with something that generates and compiles Java source code. If this process occurs before you "run" the application, you've satisfied the requirement for static type-safety.
I was more asking about reflection in which the result is always the same. I.E. Person.class.getMethod("getPhoneNumber", null) would always return the same method and it's entirely possible to resolve it at compile time.
What happens if after compiling the class containing this code, you change Person to remove the getPhoneNumber method?
The only way you can be sure that you can resolve getPhoneNumber reflectively is if you can somehow prevent Person from being changed. But you can't do that in Java. Runtime binding of classes is a fundamental part of the language.
(For record, if you did that for a method that you called non-reflectively, you would get an IncompatibleClassChangeError of some kind when the two classes were loaded ...)
It has been pointed out that in Java 8 and later you could declare your validator something like this:
public Result validateField(Object data,
SomeFunctionalInterface function,
ValidationOptions options) { ... }
where SomeFunctionalInterface corresponds to the (loosely speaking) common signature of the methods you are validating.
Then you can call it with a method reference; e.g.
validateField(data, SomeClass::someMethod, options)
This is approach is statically type-safe. You will get a compilation error if SomeClass doesn't have someMethod or if it doesn't conform to SomeFunctionalInterface.
But you can't use a string to denote the method name. Looking up a method by name would entail either reflection ... or something else that side-steps static (i.e. compile time / load time) type safety.
Java misses the syntax sugar to do something as nice as Person.phoneNumber.getter. But if Person is an interface, you could record the getter method using a dynamic proxy. You could record methods on non-final classes as well using CGLib, the same way Mockito does it.
MethodSelector<Person> selector = new MethodSelector<Person>(Person.class);
selector.select().getPhoneNumber();
validateField(data, selector.getMethod(), options);
Code for MethodSelector: https://gist.github.com/stijnvanbael/5965609
Inspired by mocking frameworks, we could dream up the following syntax:
validator.validateField(data, options).getPhoneNumber();
Result validationResult = validator.getResult();
The trick is the generic declaration:
class Validator {
public <T> T validateField(T data, options) {...}
}
Now the return type of the method is the same as your data object's type and you can use code completion (and static checking) to access all the methods, including the getter methods.
As a downside, the code isn't quite intuitive to read, since the call to the getter doesn't actually get anything, but instead instructs the validator to validate the field.
Another possible option would be to annotate the fields in your data class:
class FooData {
#Validate(new ValidationOptions(...))
private PhoneNumber phoneNumber;
}
And then just call:
FooData data;
validator.validate(data);
to validate all fields according to the annotated options.
The framework picklock lets you do the following:
class Data {
private PhoneNumber phoneNumber;
}
interface OpenData {
PhoneNumber getPhoneNumber(); //is mapped to the field phoneNumber
}
Object data = new Data();
PhoneNumber number = ObjectAccess
.unlock(data)
.features(OpenData.class)
.getPhoneNumber();
This works in a similar way setters and private methods. Of course, this is only a wrapper for reflection, but the exception does not occur at unlocking time not at call time. If you need it at build time, you could write a unit test with:
assertThat(Data.class, providesFeaturesOf(OpenData.class));
I found a way to get the Method instance using Lambdas. It works only on interface methods though currently.
It works using net.jodah:typetools which is a very lightweight library.
https://github.com/jhalterman/typetools
public final class MethodResolver {
private interface Invocable<I> {
void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable;
}
interface ZeroParameters<I, R> extends Invocable<I> {
R invoke(I instance) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance);
}
}
public static <I, R> Method toMethod0(ZeroParameters<I, R> call) {
return toMethod(ZeroParameters.class, call, 1);
}
interface OneParameters<I, P1, R> extends Invocable<I> {
R invoke(I instance, P1 p1) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance, param(parameterTypes[1]));
}
}
public static <I, P1, R> Method toMethod1(OneParameters<I, P1, R> call) {
return toMethod(OneParameters.class, call, 2);
}
interface TwoParameters<I, P1, P2, R> extends Invocable<I> {
R invoke(I instance, P1 p1, P2 p2) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance, param(parameterTypes[1]), param(parameterTypes[2]));
}
}
public static <I, P1, P2, R> Method toMethod2(TwoParameters<I, P1, P2, R> call) {
return toMethod(TwoParameters.class, call, 3);
}
private static final Map<Class<?>, Object> parameterMap = new HashMap<>();
static {
parameterMap.put(Boolean.class, false);
parameterMap.put(Byte.class, (byte) 0);
parameterMap.put(Short.class, (short) 0);
parameterMap.put(Integer.class, 0);
parameterMap.put(Long.class, (long) 0);
parameterMap.put(Float.class, (float) 0);
parameterMap.put(Double.class, (double) 0);
}
#SuppressWarnings("unchecked")
private static <T> T param(Class<?> type) {
return (T) parameterMap.get(type);
}
private static <I> Method toMethod(Class<?> callType, Invocable<I> call, int responseTypeIndex) {
Class<?>[] typeData = TypeResolver.resolveRawArguments(callType, call.getClass());
Class<?> instanceClass = typeData[0];
Class<?> responseType = responseTypeIndex != -1 ? typeData[responseTypeIndex] : Void.class;
AtomicReference<Method> ref = new AtomicReference<>();
I instance = createProxy(instanceClass, responseType, ref);
try {
call.invokeWithParams(instance, typeData);
} catch (final Throwable e) {
throw new IllegalStateException("Failed to call no-op proxy", e);
}
return ref.get();
}
#SuppressWarnings("unchecked")
private static <I> I createProxy(Class<?> instanceClass, Class<?> responseType,
AtomicReference<Method> ref) {
return (I) Proxy.newProxyInstance(MethodResolver.class.getClassLoader(),
new Class[] {instanceClass},
(proxy, method, args) -> {
ref.set(method);
return parameterMap.get(responseType);
});
}
}
Usage:
Method method = MethodResolver.toMethod2(SomeIFace::foobar);
System.out.println(method); // public abstract example.Result example.SomeIFace.foobar(java.lang.String,boolean)
Method get = MethodResolver.<Supplier, Object>toMethod0(Supplier::get);
System.out.println(get); // public abstract java.lang.Object java.util.function.Supplier.get()
Method accept = MethodResolver.<IntFunction, Integer, Object>toMethod1(IntFunction::apply);
System.out.println(accept); // public abstract java.lang.Object java.util.function.IntFunction.apply(int)
Method apply = MethodResolver.<BiFunction, Object, Object, Object>toMethod2(BiFunction::apply);
System.out.println(apply); // public abstract java.lang.Object java.util.function.BiFunction.apply(java.lang.Object,java.lang.Object)
Unfortunately you have to create a new interface and method based on the parameter count and whether the method returns void or not.
However, if you have a somewhat fixed/limited method signature/parameter types, then this becomes quite handy.

Categories