Skip to content

Instantly share code, notes, and snippets.

@Andrei-Pozolotin
Last active August 11, 2024 05:56
Show Gist options
  • Save Andrei-Pozolotin/419df7d78ca6ec4e6c28 to your computer and use it in GitHub Desktop.
Save Andrei-Pozolotin/419df7d78ca6ec4e6c28 to your computer and use it in GitHub Desktop.
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 Target2 {
private int id;
Target2(final int id) {
this.id = id;
}
private int id() {
return id;
}
private void id(final int id) {
this.id = id;
}
}
public class PrivateTargetLambdaGeneric {
static <T> T produceLambda( //
final Lookup caller, //
final Class<T> functionKlaz, //
final String functionName, //
final Class<?> functionReturn, //
final Class<?>[] functionParams, //
final MethodHandle implementationMethod //
) throws Throwable {
final MethodType factoryMethodType = MethodType
.methodType(functionKlaz);
final MethodType functionMethodType = MethodType.methodType(
functionReturn, functionParams);
final CallSite lambdaFactory = 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.
implementationMethod, // Function method implementation.
implementationMethod.type() // Function method type signature.
);
final MethodHandle factoryInvoker = lambdaFactory.getTarget();
// FIXME
/**
* <pre>
* Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected ()ToIntFunction but found ()Object
* at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:340)
* at java.lang.invoke.Invokers.checkExactType(Invokers.java:351)
* at design.PrivateTargetLambdaGeneric.produceLambda(PrivateTargetLambdaGeneric.java:64)
* at design.PrivateTargetLambdaGeneric.getterLambda(PrivateTargetLambdaGeneric.java:71)
* at design.PrivateTargetLambdaGeneric.main(PrivateTargetLambdaGeneric.java:100)
* </pre>
*/
final T lambda = (T) factoryInvoker.invokeExact();
return lambda;
}
static ToIntFunction getterLambda(final Lookup caller,
final MethodHandle getterHandle) throws Throwable {
return produceLambda(caller, ToIntFunction.class, "applyAsInt",
int.class, new Class<?>[] { Object.class }, getterHandle);
}
static ObjIntConsumer setterLambda(final Lookup caller,
final MethodHandle setterHandle) throws Throwable {
return produceLambda(caller, ObjIntConsumer.class, "accept",
void.class, new Class<?>[] { Object.class, int.class },
setterHandle);
}
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(Target2.class);
final Method getterMethod = Target2.class.getDeclaredMethod("id");
final Method setterMethod = Target2.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 Target2 target = new Target2(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.");
}
}
}
@alexandergunnarson
Copy link

alexandergunnarson commented Sep 12, 2016

I'm having the same problem as documented in your FIXME comment.

UPDATE: Thanks for the StackOverflow link! It worked!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment