Skip to content

Instantly share code, notes, and snippets.

@stijnvanbael
Last active December 19, 2015 13:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stijnvanbael/5965609 to your computer and use it in GitHub Desktop.
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.
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