Mastering Java MethodHandle: From Lookup to Performance Benchmarks

This article provides a comprehensive guide to Java MethodHandle, covering its differences from reflection, step‑by‑step creation (Lookup, MethodType, finding handles, invocation), advanced techniques like binding and dropping arguments, array spreading, and a JMH performance comparison with reflection, all demonstrated in a Spring Boot 3.4.2 environment.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Java MethodHandle: From Lookup to Performance Benchmarks

Introduction

Java reflection and MethodHandle both enable runtime method manipulation, but they differ in design goals and performance characteristics.

Practical Example Overview

The guide demonstrates creating and using a MethodHandle in four steps: creating a Lookup object, defining a MethodType, locating the method handle, and invoking it.

1. Create Lookup

Use MethodHandles.publicLookup() for public access or MethodHandles.lookup() for private/protected access.

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
MethodHandles.Lookup lookup = MethodHandles.lookup();

2. Create MethodType

MethodType describes the return type and parameter types of the target method.

MethodType mtBigDecimal = MethodType.methodType(BigDecimal.class, double.class);
MethodType mtVoid = MethodType.methodType(void.class, String.class);
MethodType mtString = MethodType.methodType(String.class, char.class, char.class);
MethodType mtList = MethodType.methodType(List.class, Object[].class);
MethodType mtBool = MethodType.methodType(boolean.class, Book.class);

3. Find MethodHandle

Examples for virtual, static, constructor, and private methods.

// virtual method
MethodHandle concatMH = lookup.findVirtual(String.class, "concat", mtString);
// static method
MethodHandle asListMH = lookup.findStatic(Arrays.class, "asList", mtList);
// constructor
MethodHandle newStringMH = lookup.findConstructor(String.class, mtVoid);
// private method via reflection
Method calcDiscountMethod = Book.class.getDeclaredMethod("calcDiscount", double.class);
calcDiscountMethod.setAccessible(true);
MethodHandle calcDiscountMH = lookup.unreflect(calcDiscountMethod);

4. Invoke MethodHandle

Three invocation styles are shown:

invoke – strict, requires exact argument count and performs necessary conversions.

invokeWithArguments – flexible, accepts a variable‑length argument list.

invokeExact – most strict, no conversions allowed.

// invoke example
String result = (String) concatMH.invoke("pacg", Character.valueOf('g'), 'k');
// invokeWithArguments example
Object discount = calcDiscountMH.invokeWithArguments(book, 0.55);
// invokeExact example
BigDecimal exact = (BigDecimal) calcDiscountMH.invokeExact(book, 0.55);

Array Argument Spreading

Use asSpreader to adapt a handle to accept an array instead of fixed arguments.

MethodHandle isPriceEqualMH = lookup.findVirtual(Book.class, "isPriceEqual", mtBool);
isPriceEqualMH = isPriceEqualMH.asSpreader(Book[].class, 1);
boolean equal = (boolean) isPriceEqualMH.invokeExact(book1, new Book[]{book2});

Enhancing MethodHandle

Bind arguments to preset values, creating a new handle that requires fewer parameters.

MethodHandle bound = concatMH.bindTo("pack_");
System.err.println(bound.invoke("xg"));

Dropping Arguments

Use MethodHandles.dropArguments to ignore extra parameters when the method signature expects fewer.

MethodHandle concatMH = lookup.findStatic(MethodHanldeDropArgumentDemo.class, "concat", mtString);
concatMH = MethodHandles.dropArguments(concatMH, 1, String.class);
String out = (String) concatMH.invokeExact("pack", "xg");

Performance Comparison

JMH benchmarks compare MethodHandle invocation styles with traditional reflection.

Benchmark                                            Mode  Cnt   Score   Error  Units
MethodHandleReflectTest.testMethodHandleInvoke       avgt    5   53.055 ±0.621 ns/op
MethodHandleReflectTest.testMethodHandleInvokeExact  avgt    5   55.394 ±0.514 ns/op
MethodHandleReflectTest.testMethodHandleInvokeWithArguments avgt    5  156.647 ±2.313 ns/op
MethodHandleReflectTest.testReflect                 avgt    5   56.372 ±0.653 ns/op
JavaperformanceReflectionMethodHandleJMH
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.