Skip to content

Instantly share code, notes, and snippets.

@Andrei-Pozolotin
Last active March 31, 2022 01:44
Show Gist options
  • Save Andrei-Pozolotin/dc8b448dc590183f5459 to your computer and use it in GitHub Desktop.
Save Andrei-Pozolotin/dc8b448dc590183f5459 to your computer and use it in GitHub Desktop.
Black magic solution for: "Java 8 access private member with lambda?" http://stackoverflow.com/questions/28184065/java-8-access-private-member-with-lambda
package design;
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.function.ObjIntConsumer;
import java.util.function.ToIntFunction;
// Black magic solution for: "Java 8 access private member with lambda?"
// http://stackoverflow.com/questions/28184065/java-8-access-private-member-with-lambda
class Target {
private int id;
Target(final int id) {
this.id = id;
}
private int id() {
return id;
}
private void id(final int id) {
this.id = id;
}
}
public class PrivateTargetLambda {
static ToIntFunction getterLambda(final Lookup caller,
final MethodHandle getterHandle) throws Throwable {
final Class<?> functionKlaz = ToIntFunction.class;
final String functionName = "applyAsInt";
final Class<?> functionReturn = int.class;
final Class<?>[] functionParams = new Class<?>[] { Object.class };
//
final MethodType factoryMethodType = MethodType
.methodType(functionKlaz);
final MethodType functionMethodType = MethodType.methodType(
functionReturn, functionParams);
final CallSite getterFactory = LambdaMetafactory.metafactory( //
caller, // Represents a lookup context.
functionName, // The name of the method to implement.
factoryMethodType, // Signature of the factory method.
functionMethodType, // Signature of function implementation.
getterHandle, // Function method implementation.
getterHandle.type() // Function method type signature.
);
final MethodHandle getterInvoker = getterFactory.getTarget();
final ToIntFunction getterLambda = (ToIntFunction) getterInvoker
.invokeExact();
return getterLambda;
}
static ObjIntConsumer setterLambda(final Lookup caller,
final MethodHandle setterHandle) throws Throwable {
final Class<?> functionKlaz = ObjIntConsumer.class;
final String functionName = "accept";
final Class<?> functionReturn = void.class;
final Class<?>[] functionParams = new Class<?>[] { Object.class,
int.class };
final MethodType factoryMethodType = MethodType
.methodType(functionKlaz);
final MethodType functionMethodType = MethodType.methodType(
functionReturn, functionParams);
final CallSite setterFactory = LambdaMetafactory.metafactory( //
caller, // Represents a lookup context.
functionName, // The name of the method to implement.
factoryMethodType, // Signature of the factory method.
functionMethodType, // Signature of function implementation.
setterHandle, // Function method implementation.
setterHandle.type() // Function method type signature.
);
final MethodHandle setterInvoker = setterFactory.getTarget();
final ObjIntConsumer setterLambda = (ObjIntConsumer) setterInvoker
.invokeExact();
return setterLambda;
}
public static void main(final String... args) throws Throwable {
// Define black magic.
final Lookup original = MethodHandles.lookup();
final Field internal = Lookup.class.getDeclaredField("IMPL_LOOKUP");
internal.setAccessible(true);
final Lookup trusted = (Lookup) internal.get(original);
// Invoke black magic.
final Lookup caller = trusted.in(Target.class);
final Method getterMethod = Target.class.getDeclaredMethod("id");
final Method setterMethod = Target.class.getDeclaredMethod("id",
int.class);
final MethodHandle getterHandle = caller.unreflect(getterMethod);
final MethodHandle setterHandle = caller.unreflect(setterMethod);
final ToIntFunction getterLambda = getterLambda(caller, getterHandle);
final ObjIntConsumer setterLambda = setterLambda(caller, setterHandle);
final int set1 = 123;
final Target target = new Target(set1);
final int get1 = getterLambda.applyAsInt(target);
if (get1 != set1) {
throw new Error("Getter failure.");
} else {
System.out.println("Getter success.");
}
final int set2 = 456;
setterLambda.accept(target, set2);
final int get2 = getterLambda.applyAsInt(target);
if (get2 != set2) {
throw new Error("Setter failure.");
} else {
System.out.println("Setter success.");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment