Last active
December 19, 2015 13:58
-
-
Save stijnvanbael/5965609 to your computer and use it in GitHub Desktop.
Provides a safe, compiler-checked way to obtain a Java Method reference from an interface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.util.HashMap; | |
import java.util.Map; | |
/** | |
* Provides a safe, compiler-checked way to obtain a {@code Method} reference from an interface. eg: | |
* <pre> | |
* {@code | |
* MethodSelector<MyClass> methodSelector = new MethodSelector<MyClass>(MyClass.class); | |
* methodSelector.select().myMethod("some string"); | |
* Method method = methodSelector.getMethod(); | |
* } | |
* </pre> | |
*/ | |
public final class MethodSelector<T> { | |
private static final Map<Class<?>, Object> PRIMITIVE_RETURN_VALUES = new HashMap<Class<?>, Object>(); | |
static { | |
PRIMITIVE_RETURN_VALUES.put(boolean.class, false); | |
PRIMITIVE_RETURN_VALUES.put(byte.class, (byte) 0); | |
PRIMITIVE_RETURN_VALUES.put(char.class, (char) 0); | |
PRIMITIVE_RETURN_VALUES.put(short.class, (short) 0); | |
PRIMITIVE_RETURN_VALUES.put(int.class, 0); | |
PRIMITIVE_RETURN_VALUES.put(long.class, 0L); | |
PRIMITIVE_RETURN_VALUES.put(float.class, 0F); | |
PRIMITIVE_RETURN_VALUES.put(double.class, 0D); | |
} | |
private Class<T> interfaceType; | |
private Method method; | |
public MethodSelector(Class<T> interfaceType) { | |
if(!interfaceType.isInterface()) { | |
throw new IllegalArgumentException("Only interfaces are supported."); | |
} | |
this.interfaceType = interfaceType; | |
} | |
/** | |
* Selects the method by chaining the method call with the desired argument types to this method. | |
* Argument values do not matter. eg: {@code methodSelector.select().myMethod("some string")} | |
* @return a proxy for intercepting the method call | |
*/ | |
public T select() { | |
return interfaceType.cast(Proxy.newProxyInstance(MethodSelector.class.getClassLoader(), | |
new Class[]{ interfaceType }, new InvocationHandler() { | |
@Override | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
MethodSelector.this.method = method; | |
if("toString".equals(method.getName())) { | |
return "MethodSelector proxy for " + interfaceType; | |
} | |
// Prevents NullPointerExceptions on methods returning primitive types | |
if(PRIMITIVE_RETURN_VALUES.containsKey(method.getReturnType())) { | |
return PRIMITIVE_RETURN_VALUES.get(method.getReturnType()); | |
} | |
return null; | |
} | |
})); | |
} | |
public Method getMethod() { | |
if(method == null) { | |
throw new IllegalStateException("Call select() first, followed by the method you want to select."); | |
} | |
return method; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment