Skip to content

Instantly share code, notes, and snippets.

@jdigger
Created January 2, 2015 23:52
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 jdigger/f4183e36c52a073b469d to your computer and use it in GitHub Desktop.
Save jdigger/f4183e36c52a073b469d to your computer and use it in GitHub Desktop.
Functions for working with MethodHandles in Proxy
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Functions for doing reflection.
*/
public final class Reflection {
// used by privateMethodHandleLookup(..)
private final static Constructor<MethodHandles.Lookup> LOOKUP_CONSTRUCTOR;
static {
try {
LOOKUP_CONSTRUCTOR = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
if (!LOOKUP_CONSTRUCTOR.isAccessible()) {
LOOKUP_CONSTRUCTOR.setAccessible(true);
}
}
catch (NoSuchMethodException exp) {
// should be impossible, but...
throw new IllegalStateException(exp);
}
}
/**
* Returns a {@link MethodHandles.Lookup} that doesn't do "access" checks (since it allows private calls).
* A work-around for Proxy classes to access "default" interface methods.
* <p>
* See http://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/
*
* @param declaringClass the class to allow access to
* @return never null
*/
@Nonnull
public static MethodHandles.Lookup privateMethodHandleLookup(@Nonnull Class<?> declaringClass) {
try {
return LOOKUP_CONSTRUCTOR.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE);
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
/**
* Invokes an interface default-method on the given instance.
*
* @param instance the instance to invoke against
* @param method the default method on an interface
* @param args the arguments to pass to the method
* @param <T> the return type
* @return the result of invoking the method
* @throws Throwable
*/
@Nonnull
@SuppressWarnings("unchecked")
public static <T> T invokeDefaultMethod(@Nonnull Object instance,
@Nonnull Method method,
@Nullable Object[] args) throws Throwable {
return (T)getDefaultMethodHandle(method).
bindTo(instance).
invokeWithArguments(args);
}
/**
* Returns a {@link MethodHandle} to the specified interface default-method
*
* @param method the default method on an interface
* @return never null
*/
@Nonnull
public static MethodHandle getDefaultMethodHandle(@Nonnull Method method) {
Class<?> declaringClass = method.getDeclaringClass();
try {
return privateMethodHandleLookup(declaringClass).unreflectSpecial(method, declaringClass);
}
catch (IllegalAccessException e) {
throw new IllegalArgumentException("Did not pass in an interface method: " + method);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment