Last active
August 11, 2024 05:56
-
-
Save Andrei-Pozolotin/419df7d78ca6ec4e6c28 to your computer and use it in GitHub Desktop.
Java 8 generic LambdaMetafactory? http://stackoverflow.com/questions/28196829/java-8-generic-lambdametafactory
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 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."); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm having the same problem as documented in your FIXME comment.
UPDATE: Thanks for the StackOverflow link! It worked!