This is library that can be used to generate lambdas from method handles with any kind of
access modifier without any performance loss. This includes methods, constructors, field accessors
(getter, setter) and any other MethodHandle
that can be constructed.
Every available MethodHandle
can be implemented by a functional interface. But only if the two method signatures match. Object types will be auto casted to the target type if possible, the same goes for boxing/unboxing of primitive types.
Non-static methods will always take an extra parameter which represents the target object of the method. This is always the first parameter.
Note: Generating functions requires that you have access to the target class.
Since Java 9, this is even more important because the module your code is located in must be able to access the module of the target class, see
MethodHandles#privateLookupIn
for more info.
Consider the following class which holds a static setter function.
class MyObject {
private static void set(int value) {
// ...
}
}
If you want to generate a function for the static set(int)
method, you need to use
a interface which takes one parameter (value
) and return void
to reflect
the method signature. The IntConsumer
is a good choice available within the
Java 8 API.
final MethodHandles.Lookup lookup = MethodHandlesExtensions.privateLookupIn(MyObject.class, MethodHandles.lookup());
final MethodHandle methodHandle = lookup.findStatic(MyObject.class, "set", MethodType.methodType(void.class, int.class));
final IntConsumer setter = LambdaFactory.create(LambdaType.of(IntConsumer.class), methodHandle);
setter.accept(1000);
Consider the following class which holds a setter function.
class MyObject {
private void set(int value) {
// ...
}
}
If you want to generate a function for the static set(int)
method, you need to use
a interface which takes two parameters:
- The target of where the method is declared, this will be the target object on which the method is invoked. E.g.
target.set(value)
- The value that will be passed to the
set
method.
the first one is the target where the method is declared, MyObject
(value
) and return void
to reflect
the method signature. The ObjIntConsumer
is a good choice available within the
Java 8 API.
final MethodHandles.Lookup lookup = MethodHandlesExtensions.privateLookupIn(MyObject.class, MethodHandles.lookup());
final MethodHandle methodHandle = lookup.findVirtual(MyObject.class, "set", MethodType.methodType(void.class, int.class));
final ObjIntConsumer<MyObject> setter = LambdaFactory.create(new LambdaType<ObjIntConsumer<MyObject>>() {}, methodHandle);
final MyObject myObject = new MyObject();
setter.accept(myObject, 1000);
Below you can find a few benchmarks. lmbda
will be generated at runtime, all the other
ones are implemented manually. The generated structure of the lmbda
is almost the
same as the static_mh
.
The following benchmark implements a lambda function to access a int
field, a ToIntFunction
is implemented in this case to avoid boxing/unboxing.
The benchmark was run on an AMD Ryzen 9 5900X CPU and using JDK 16.0.2
Benchmark | Mode | Cnt | Score | Error | Units |
---|---|---|---|---|---|
IntGetterFieldBenchmark.direct | avgt | 15 | 1.304 | ± 0.008 | ns/op |
IntGetterFieldBenchmark.fieldConst | avgt | 15 | 2.698 | ± 0.061 | ns/op |
IntGetterFieldBenchmark.fieldDyn | avgt | 15 | 2.986 | ± 0.005 | ns/op |
IntGetterFieldBenchmark.lmbda | avgt | 15 | 1.299 | ± 0.005 | ns/op |
IntGetterFieldBenchmark.methodHandleConst | avgt | 15 | 1.300 | ± 0.004 | ns/op |
IntGetterFieldBenchmark.methodHandleDyn | avgt | 15 | 3.013 | ± 0.018 | ns/op |
IntGetterFieldBenchmark.methodHandleProxy | avgt | 15 | 7.530 | ± 0.185 | ns/op |
IntGetterFieldBenchmark.plain | avgt | 15 | 1.304 | ± 0.006 | ns/op |
This benchmark returns a Integer
through the getValue
method from a target object.
Benchmark | Mode | Cnt | Score | Error | Units |
---|---|---|---|---|---|
IntGetterMethodBenchmark.direct | avgt | 15 | 1.372 | ± 0.003 | ns/op |
IntGetterMethodBenchmark.lambdaMetafactory | avgt | 15 | 1.387 | ± 0.024 | ns/op |
IntGetterMethodBenchmark.lmbda | avgt | 15 | 1.370 | ± 0.004 | ns/op |
IntGetterMethodBenchmark.methodConst | avgt | 15 | 3.731 | ± 0.106 | ns/op |
IntGetterMethodBenchmark.methodDyn | avgt | 15 | 4.164 | ± 0.139 | ns/op |
IntGetterMethodBenchmark.methodHandleConst | avgt | 15 | 1.612 | ± 0.379 | ns/op |
IntGetterMethodBenchmark.methodHandleDyn | avgt | 15 | 3.852 | ± 0.116 | ns/op |
IntGetterMethodBenchmark.methodHandleProxy | avgt | 15 | 8.085 | ± 0.070 | ns/op |
IntGetterMethodBenchmark.plain | avgt | 15 | 1.398 | ± 0.025 | ns/op |