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).
Related
Java uses type erasure wherever applicable, but, of course, when invoking methods the parameter types must match.
How to avoid a ClassCastException when I cannot ensure the type at compile time? Example:
class KeyedHashSet<K,E> // implements Set<E>
{ final Map<K,E> map = new HashMap<>();
final Function<E,K> keyExtractor; // extracts intrusive key from value
public KeyedHashSet(Function<E,K> keyExtractor)
{ this.keyExtractor = keyExtractor; }
public boolean contains(Object o)
{ if (o != null)
try
{ #SuppressWarnings("unchecked") // <- BAD
K key = keyExtractor.apply((E)o);
E elem = map.get(key);
return elem != null && elem.equals(o);
} catch (ClassCastException ex) // <- EVIL!!!
{}
return false;
}
// more methods
}
The method Set.contains takes an arbitrary object as parameter. But I cannot extract the key required for my hash lookup from an arbitrary object. This only works for objects of type E.
In fact I am not interested in the key when the object is not of type E because in this case I am sure that the collection does not contain the object.
But the above work around with catching the ClassCastException has several drawbacks:
First of all, one should not throw an exception when this is a normal program path.
Secondly, I might catch a ClassCastException thrown from deep inside the keyExtractor implementation, which is not intended.
Is it possible to check the type of o against the parameter of keyExtractor before the call to .apply and without unreasonable runtime overhead?
Note: I know that the above design requires E to have an immutable key. But this is no big deal and happens quite often.
How could a ClassCastException happen after Erasure?
After erasure is applied it can be hard to determine if an unchecked cast will trigger a ClassCastException. After all, the types are reduced to Object; why would casting to Object throw a casting exception? One cause for this error is generic code that calls non-generic implementations, where the types are explicitly stated. Take this example:
class Test<K> {
public void foo(Object o) {
bar((K) o);
}
public void bar(K k) {
System.out.println(k);
}
public static void main(String[] args) {
Test<Integer> test = new Test<>();
test.foo("hello");
}
}
The above example will still print "hello" correctly even though the generic type argument was an Integer. After erasure the method bar only requires an object:
public bar(Ljava/lang/Object;)V
If we extend Test and override bar where the type is explicit, then we will produce an error.
class TestInteger extends Test<Integer> {
#Override
public void bar(Integer k) {
super.bar(k);
}
public static void main(String[] args) {
Test<Integer> test = new TestInteger();
test.foo("hello");
}
}
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at TestInteger.bar(TestInteger.java:17)
at Test.foo(TestInteger.java:9)
at TestInteger.foo(TestInteger.java:17)
at TestInteger.main(TestInteger.java:24)
In this child class, the method being overridden has a different signature than the method produced by Test<K>. The compiler creates a new overloaded method, called a synthetic or bridge method, in order to call bar as written in TestInteger. This bridge method is where the ClassCastException happens. It will look like bellow:
public void bar(Object k) {
bar((Integer) k); //java.lang.String cannot be cast to java.lang.Integer
}
public void bar(Integer k) {
System.out.println(k);
}
In your example, somewhere within the call to keyExtractor.apply((E)o) there lies a signature which relies on the explicit type causing the casting exception.
Is it possible to check the type before casting?
Yes it is possible, but you will have to provide your KeyedHashSet class with extra data. You cannot directly get the Class object associated with a type parameter.
One way is to inject a Class type into your container and call isInstance:
This method is the dynamic equivalent of the Java language instanceof operator. The method returns true if the specified Object argument is non-null and can be cast to the reference type represented by this Class object without raising a ClassCastException. It returns false otherwise.
public class Test<K> {
final Class<K> clazz;
Test(Class<K> clazz) { this.clazz = clazz; }
public void foo(Object o) {
if (clazz.isInstance(o)) {
bar((K) o);
}
}
...
Test<Integer> test = new Test<>(Integer.class);
test.foo("string");
You could also use some validation strategy where the instance check is performed:
public class Test<K> {
final Function<Object, Boolean> validator;
Test(Function<Object, Boolean> validator) { this.validator = validator; }
public void foo(Object o) {
if (validator.apply(o)) {
bar((K) o);
}
}
...
Test<Integer> test = new Test<>(k -> k instanceof Integer);
test.foo("string");
Another option could be to move the type checking inside the Function<E,K> keyExtractor instance and have the type parameters become Function<Object,K> keyExtractor, returning null if the type was incorrect.
It would also be theoretically possible to reflectively examine method signatures for keyExtractor and get a Class instance, but it is not guaranteed that it's implementation will explicitly define the type parameters either.
Will checking instanceof slow down my application?
The execution time of isInstance is actually quite fast. There's an interesting article which experimentally compares the speeds of a try-catch with unsafe cast to an isInstance solution. In the results of the experiment, the solution which explicitly checks for the type is only marginally slower than the unsafe solution.
Given that the performance penalty is so low I would opt to go the safe route and add class checking to your contains method. If you keep the try-catch solution as is, you may end up masking future bugs caused by the implementation of keyExtractor.apply, map.get, elem.equals, etc.
Given the following interface:
public interface GenericInterface<T> {
T getValue();
void setValue(T newVal);
}
And the following impl:
public class FixedImpl implements GenericInterface<String> {
String value;
public FixedImpl(String value) {
this.value = value;
}
#Override
public String getValue() {
return value;
}
#Override
public void setValue(String newVal) {
value = newVal;
}
}
I want to be able to determine that in the case of FixedImpl, String.class is the value for GenericInterface.T by interrogating FixedImpl.class.
My current idea:
Find a method name in GenericInterface that returns a <T> - in this case, there's "getValue".
Go through all the methods declared in FixedImpl.class with the same name, and collect all the different return types.
The return type farthest from Object is my value for GenericInterface.T.
But there's a couple of issues with this process:
It will only work for generic types containing a method that returns <T>. You can't safely do the same trick using setValue(T), because method overloading by parameter / arity is possible to do in Java source. It only works for T getValue() because overloading by return value isn't (unless I'm mistaken).
It might have weird interactions with Java 8 default methods, or a generic method implementation in a (still generic) possibly abstract superclass.
It's kinda kludgey.
Can anybody point me to an easier / more surefire way to get the same information? I can't seem to find one, but I thought I'd ask the superior intellects of the toobs :)
NB: If you're wondering why I'd need this, it's because I want to programatically construct mocks of container classes with similar hard-coded type parameters, but POJO values rather than simple Strings.
EDIT: I eventually worked out the following solution (before seeing #stony-zhang's):
public static <G> List<Class> getConcreteTypes(Class<? extends G> implClass, Class<G> genericClass) {
List<Class> concreteTypes = new ArrayList<Class>();
for (Type type : implClass.getGenericInterfaces()) {
if (!(type instanceof ParameterizedTypeImpl)) continue;
ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) type;
if (parameterizedType.getRawType() != genericClass) continue;
for (Object arg : parameterizedType.getActualTypeArguments()) {
if (!(arg instanceof Class))
throw new IllegalArgumentException("Class " + implClass + " not concrete for generic type " + genericClass);
concreteTypes.add((Class) arg);
}
}
return concreteTypes;
}
You can get the the class of T by the following way, in the interface add a method getMessageClass(), and in the FixedImpl add the implemented method,
#SuppressWarnings("rawtypes")
public Class getMessageClass() {
int index =0; //In the case, you only have a generic type, so index is 0 to get the first one.
Type genType = getClass().getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
throw new RuntimeException("Index outof bounds");
}
if (!(params[index] instanceof Class)) {
return Object.class;
}
return (Class) params[index];
}
In you case, if you have multiple subclass, to use it, create one abstract class to implement the interface GenericInterface, and then the all subclass extends from the new abstract class,
public class abstract abstractImpl<T> implements implements GenericInterface<T> {
#SuppressWarnings("rawtypes")
#Override
public Class getMessageClass() {
...............
}
}
Remember type erasure. At runtime, there is no type information about your generics anymore, unless you specify it yourself. And this is what you should do. Add this to your interface:
Class<T> getTypeOfT();
And add this to your FixedImpl:
#Override
public Class<String> getTypeOfT()
{
return String.class;
}
That way, you can always call getTypeOfT() on your GenericInterface<T> implementations and find out what type you are dealing with.
I don't think that you will be able to get reliable result because of Type Erasure:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Your approach of of using the types of objects returned may at first seem alright, but beyond the issues you have pointed out there is no way (at runtime) to know if The return type farthest from Object is my value for GenericInterface.T.
My suggestion would be to use some kind of configuration XML which could be generated at build time based on the java source (using a build tool such as Ant), which would in turn be used to create Mock objects, or you could simply generate the tests based off the source at buildtime.
If you don't mind changing your runtime code for the purposes of testing, Jan Doereenhaus' answer suggests a simple hard-coded mechanism for retrieving the type
EDIT:
Consider the scenario:
public class FixedImpl implements GenericInterface<SomeClass> {
#Override
public SomeClass getValue() {
return new SomeClass();
}
}
public class FixedImpl2 extends FixedImpl {
#Override
public SomeClass getValue()
{
return new SomeSubClass();
}
}
From this example, you can see that the sub class of FixedImpl is able to return a subclass of T (which is further down the inheritance hierarchy from Object)
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?
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();
}
}
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.