eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Introduction

In this article, we’re going to explore an important API that was introduced in Java 7 and enhanced in the following versions, the java.lang.invoke.MethodHandles.

In particular, we’ll learn what method handles are, how to create them and how to use them.

2. What Are Method Handles?

Coming to its definition, as stated in the API documentation:

A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values.

In a simpler way, method handles are a low-level mechanism for finding, adapting and invoking methods.

Method handles are immutable and have no visible state.

For creating and using a MethodHandle, 4 steps are required:

  • Creating the lookup
  • Creating the method type
  • Finding the method handle
  • Invoking the method handle

2.1. Method Handles vs Reflection

Method handles were introduced in order to work alongside the existing java.lang.reflect API, as they serve different purposes and have different characteristics.

From a performance standpoint, the MethodHandles API can be much faster than the Reflection API since the access checks are made at creation time rather than at execution time. This difference gets amplified if a security manager is present, since member and class lookups are subject to additional checks.

However, considering that performance isn’t the only suitability measure for a task, we have also to consider that the MethodHandles API is harder to use due to the lack of mechanisms such as member class enumeration, accessibility flags inspection and more.

Even so, the MethodHandles API offers the possibility to curry methods, change the types of parameters and change their order.

Having a clear definition and goals of the MethodHandles API, we can now begin to work with them, starting from the lookup.

3. Creating the Lookup

The first thing to do when we want to create a method handle is to retrieve the lookup, the factory object that is responsible for creating method handles for methods, constructors, and fields, that are visible to the lookup class.

Through the MethodHandles API, it’s possible to create the lookup object, with different access modes.

Let’s create the lookup that provides access to public methods:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();

However, in case we want to have access also to private and protected methods, we can use, instead, the lookup() method:

MethodHandles.Lookup lookup = MethodHandles.lookup();

4. Creating a MethodType

In order to be able to create the MethodHandle, the lookup object requires a definition of its type and this is achieved through the MethodType class.

In particular, a MethodType represents the arguments and return type accepted and returned by a method handle or passed and expected by a method handle caller.

The structure of a MethodType is simple and it’s formed by a return type together with an appropriate number of parameter types that must be properly matched between a method handle and all its callers.

In the same way as MethodHandle, even the instances of a MethodType are immutable.

Let’s see how it’s possible to define a MethodType that specifies a java.util.List class as return type and an Object array as input type:

MethodType mt = MethodType.methodType(List.class, Object[].class);

In case the method returns a primitive type or void as its return type, we will use the class representing those types (void.class, int.class …).

Let’s define a MethodType that returns an int value and accepts an Object:

MethodType mt = MethodType.methodType(int.class, Object.class);

We can now proceed to create MethodHandle.

5. Finding a MethodHandle

Once we’ve defined our method type, in order to create a MethodHandle, we have to find it through the lookup or publicLookup object, providing also the origin class and the method name.

In particular, the lookup factory provides a set of methods that allow us to find the method handle in an appropriate way considering the scope of our method. Starting with the simplest scenario, let’s explore the principal ones.

5.1. Method Handle for Methods

Using the findVirtual() method allow us to create a MethodHandle for an object method. Let’s create one, based on the concat() method of the String class:

MethodType mt = MethodType.methodType(String.class, String.class);
MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt);

5.2. Method Handle for Static Methods

When we want to gain access to a static method, we can instead use the findStatic() method:

MethodType mt = MethodType.methodType(List.class, Object[].class);

MethodHandle asListMH = publicLookup.findStatic(Arrays.class, "asList", mt);

In this case, we created a method handle that converts an array of Objects to a List of them.

5.3. Method Handle for Constructors

Gaining access to a constructor can be done using the findConstructor() method.

Let’s create a method handles that behaves as the constructor of the Integer class, accepting a String attribute:

MethodType mt = MethodType.methodType(void.class, String.class);

MethodHandle newIntegerMH = publicLookup.findConstructor(Integer.class, mt);

5.4. Method Handle for Fields

Using a method handle it’s possible to gain access also to fields.

Let’s start defining the Book class:

public class Book {
    
    String id;
    String title;

    // constructor

}

Having as precondition a direct access visibility between the method handle and the declared property, we can create a method handle that behaves as a getter:

MethodHandle getTitleMH = lookup.findGetter(Book.class, "title", String.class);

For further information on handling variables/fields, give a look at the Java 9 Variable Handles Demystified, where we discuss the java.lang.invoke.VarHandle API, added in Java 9.

5.5. Method Handle for Private Methods

Creating a method handle for a private method can be done, with the help of the java.lang.reflect API.

Let’s start adding a private method to the Book class:

private String formatBook() {
    return id + " > " + title;
}

Now we can create a method handle that behaves exactly as the formatBook() method:

Method formatBookMethod = Book.class.getDeclaredMethod("formatBook");
formatBookMethod.setAccessible(true);

MethodHandle formatBookMH = lookup.unreflect(formatBookMethod);

6. Invoking a Method Handle

Once we’ve created our method handles, use them is the next step. In particular, the MethodHandle class provides 3 different way to execute a method handle: invoke(), invokeWithArugments() and invokeExact().

Let’s start with the invoke option.

6.1. Invoking a Method Handle

When using the invoke() method, we enforce the number of the arguments (arity) to be fixed but we allow the performing of casting and boxing/unboxing of the arguments and return types.

Let’s see how it’s possible to use the invoke() with a boxed argument:

MethodType mt = MethodType.methodType(String.class, char.class, char.class);
MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt);

String output = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');

assertEquals("java", output);

In this case, the replaceMH requires char arguments, but the invoke() performs an unboxing on the Character argument before its execution.

6.2. Invoking With Arguments

Invoking a method handle using the invokeWithArguments method, is the least restrictive of the three options.

In fact, it allows a variable arity invocation, in addition to the casting and boxing/unboxing of the arguments and of the return types.

Coming to practice, this allows us to create a List of Integer starting from an array of int values:

MethodType mt = MethodType.methodType(List.class, Object[].class);
MethodHandle asList = publicLookup.findStatic(Arrays.class, "asList", mt);

List<Integer> list = (List<Integer>) asList.invokeWithArguments(1,2);

assertThat(Arrays.asList(1,2), is(list));

6.3. Invoking Exact

In case we want to be more restrictive in the way we execute a method handle (number of arguments and their type), we have to use the invokeExact() method.

In fact, it doesn’t provide any casting to the class provided and requires a fixed number of arguments.

Let’s see how we can sum two int values using a method handle:

MethodType mt = MethodType.methodType(int.class, int.class, int.class);
MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt);

int sum = (int) sumMH.invokeExact(1, 11);

assertEquals(12, sum);

If in this case, we decide to pass to the invokeExact method a number that isn’t an int, the invocation will lead to WrongMethodTypeException.

7. Working With Array

MethodHandles aren’t intended to work only with fields or objects, but also with arrays. As a matter of fact, with the asSpreader() API, it’s possible to make an array-spreading method handle.

In this case, the method handle accepts an array argument, spreading its elements as positional arguments, and optionally the length of the array.

Let’s see how we can spread a method handle to check if the elements within an array are equals:

MethodType mt = MethodType.methodType(boolean.class, Object.class);
MethodHandle equals = publicLookup.findVirtual(String.class, "equals", mt);

MethodHandle methodHandle = equals.asSpreader(Object[].class, 2);

assertTrue((boolean) methodHandle.invoke(new Object[] { "java", "java" }));

8. Enhancing a Method Handle

Once we’ve defined a method handle, it’s possible to enhance it by binding the method handle to an argument without actually invoking it.

For example, in Java 9, this kind of behaviour is used to optimize String concatenation.

Let’s see how we can perform a concatenation, binding a suffix to our concatMH:

MethodType mt = MethodType.methodType(String.class, String.class);
MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt);

MethodHandle bindedConcatMH = concatMH.bindTo("Hello ");

assertEquals("Hello World!", bindedConcatMH.invoke("World!"));

9. Java 9 Enhancements

With Java 9, few enhancements were made to the MethodHandles API with the aim to make it much easier to use.

The enhancements affected 3 main topics:

  • Lookup functions – allowing class lookups from different contexts and support non-abstract methods in interfaces
  • Argument handling – improving the argument folding, argument collecting and argument spreading functionalities
  • Additional combinations – adding loops (loop, whileLoop, doWhileLoop…) and a better exception handling support with the tryFinally

These changes resulted in few additional benefits:

  • Increased JVM compiler optimizations
  • Instantiation reduction
  • Enabled precision in the usage of the MethodHandles API

Details of the enhancements made are available at the MethodHandles API Javadoc.

10. Conclusion

In this article, we covered the MethodHandles API, what they’re and how we can use them.

We also discussed how it relates to the Reflection API and since the method handles allow low-level operations, it should be better to avoid using them, unless they fit perfectly the scope of the job.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)