Type-safe method reflection in Java

2019-04-30 13:09发布

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

8条回答
Emotional °昔
2楼-- · 2019-04-30 13:52

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.

查看更多
冷血范
3楼-- · 2019-04-30 13:57

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)) {
    obj.getClass().getDeclaredMethod("foo", ...);
} else {
    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 ...)

查看更多
登录 后发表回答