Last active
March 31, 2022 01:44
-
-
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
This file contains 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
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