Created
January 2, 2015 23:52
-
-
Save jdigger/f4183e36c52a073b469d to your computer and use it in GitHub Desktop.
Functions for working with MethodHandles in Proxy
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 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