-
Notifications
You must be signed in to change notification settings - Fork 2
LambdaMetafactory
Brian S. O'Neill edited this page Dec 24, 2024
·
9 revisions
The LambdaMetafactory class is used to bootstrap dynamic methods for Java lambda functions. It's one of the more complex features of the "indy" framework, but this example provides a starting point.
Consider this Java code which invokes a lambda function:
public void hello() {
exec(() -> {
System.out.println("hello, world");
});
}
private static void exec(Runnable r) {
r.run();
}
The Java compiler desugars the hello
method into this:
public void hello() {
// Use LambdaMetafactory to define an implementation of Runnable, which
// is then passed to the exec method. One of the bootstrap parameters
// refers to the generated "lambda$hello$0" delegate method.
exec(InvokeDynamic #0:run:()Ljava/lang/Runnable;)
}
private static void lambda$hello$0() {
System.out.println("hello, world");
}
The following code generates a complete class which can run the example.
ClassMaker cm = ClassMaker.begin().public_();
// Need a default constructor.
cm.addConstructor().public_();
// Define a private "exec" method which takes a Runnable.
{
MethodMaker mm = cm.addMethod(void.class, "exec", Runnable.class).private_().static_();
mm.param(0).invoke("run");
}
// Define a private "desugared" lambda implementation method. Mark it as synthetic to help
// Java decompilers understand what's going on.
{
MethodMaker mm = cm.addMethod(void.class, "lambda$hello$0").private_().static_().synthetic();
mm.var(System.class).field("out").invoke("println", "hello, world");
}
// Define a public method which invokes a lambda function...
MethodMaker mm = cm.addMethod(void.class, "hello").public_();
// LambdaMetafactory stuff...
MethodType mt = MethodType.methodType(void.class);
var implMethod = mm.this_().methodHandle(void.class, "lambda$hello$0");
var bootstrap = mm.var(LambdaMetafactory.class).indy("metafactory", mt, implMethod, mt);
// Note: Extra params are needed if lambda implementation method isn't static.
var function = bootstrap.invoke(Runnable.class, "run"); // , null, mm.this_());
// Invoke the Runnable function.
mm.invoke("exec", function);
// Finish the class and run it.
var clazz = cm.finish();
var instance = clazz.getConstructor().newInstance();
clazz.getMethod("hello").invoke(instance);