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

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

In this tutorial, we’ll learn how to map one enum type to another using MapStruct, mapping enums to built-in Java types such as int and String, and vice versa.

2. Maven

Let’s add the below dependency to our Maven pom.xml:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.6.0.Beta1</version> 
</dependency>

The latest stable release of MapStruct is available from the Maven Central Repository.

3. Mapping One Enum to Another

In this section, we’ll learn to perform various mappings.

3.1. Understanding the Use Case

Now, let us see a few real-world scenarios.

In REST API response mapping, MapStruct converts external API status codes into our application’s internal status enums.

For data transformation in microservices, MapStruct facilitates smooth data exchange between services by mapping similar enums.

Integration with third-party libraries often involves dealing with third-party enums. MapStruct simplifies this by converting them into our application’s enums.

3.2. Implementing the Mapping With MapStruct

To configure the mapping of source constant values to target constant values, we use the @ValueMapping MapStruct annotation. It maps based on names. However, we can also map a constant from the source enum to a constant with a different name in the target enum type. For instance, we can map a source enum “Go” to the target enum “Move“.

It’s also possible to map several constants from the source enum to the same constant in the target type.

TrafficSignal enum represents a traffic signal. The external service we interact with uses the RoadSign enum. The mapper would convert the enums to each other.

Let’s define the traffic signal enum:

public enum TrafficSignal {
    Off, Stop, Go
}

Let’s define the road sign enum:

public enum RoadSign {
    Off, Halt, Move
}

Let’s implement the @Mapper:

@Mapper
public interface TrafficSignalMapper {
    TrafficSignalMapper INSTANCE = Mappers.getMapper(TrafficSignalMapper.class);

    @ValueMapping(target = "Off", source = "Off")
    @ValueMapping(target = "Go", source = "Move")
    @ValueMapping(target = "Stop", source = "Halt")
    TrafficSignal toTrafficSignal(RoadSign source);
}

@Mapper defines a MapStruct mapper called TrafficSignalMapper to convert enums to TrafficSignal. Its methods represent a mapping operation.

The @ValueMapping annotations within the interface specify explicit mappings between enum values. For example,  @ValueMapping(target = “Go”, source = “Move”) maps the Move enum to Go enum in TrafficSignal, and so on.

We need to make sure to map all enum values from source to target for complete coverage and prevent unexpected behavior.

Here’s the test for it:

@Test
void whenRoadSignIsMapped_thenGetTrafficSignal() {
    RoadSign source = RoadSign.Move;
    TrafficSignal target = TrafficSignalMapper.INSTANCE.toTrafficSignal(source);
    assertEquals(TrafficSignal.Go, target);
}

It verifies the mapping of RoadSign.Move to TrafficSignal.Go.

We must test mapping methods thoroughly with unit tests to ensure accurate behavior and detect potential issues.

4. Mapping String to Enum

Let’s convert text literal values into enum values.

4.1. Understanding the Use Case

Our application collects user input as strings. We map these strings to enum values to represent different commands or options. For example, we map “add” to Operation.ADD, “subtract” to Operation.SUBTRACT, etc.

We specify settings as strings in the application configuration. We map these strings to enum values to ensure type-safe configuration. For instance, we map “EXEC” to Mode.EXEC, “TEST” to Mode.TEST, etc.

We map external API strings to enum values in our application. For instance, we map “active” to Status.ACTIVE, “inactive” to Status.INACTIVE, etc.

4.2. Implementing the Mapping With MapStruct

Let’s use @ValueMapping to map each signal:

@ValueMapping(target = "Off", source = "Off")
@ValueMapping(target = "Go", source = "Move")
@ValueMapping(target = "Stop", source = "Halt")
TrafficSignal stringToTrafficSignal(String source);

Here’s the test for it:

@Test
void whenStringIsMapped_thenGetTrafficSignal() {
    String source = RoadSign.Move.name();
    TrafficSignal target = TrafficSignalMapper.INSTANCE.stringToTrafficSignal(source);
    assertEquals(TrafficSignal.Go, target);
}

It verifies “Move” maps to TrafficSignal.Go.

5. Handling Custom Name Transformation

Enum names may differ only by naming convention. It may follow a different case, prefix, or suffix convention. For example, a signal could be Go, go, GO, Go_Value, Value_Go.

5.1. Applying Suffix to Source Enum

We apply a suffix to the source enum to get the target enum. For instance, Go becomes Go_Value:

public enum TrafficSignalSuffixed { Off_Value, Stop_Value, Go_Value }

Let’s define the mapping:

@EnumMapping(nameTransformationStrategy = MappingConstants.SUFFIX_TRANSFORMATION, configuration = "_Value")
TrafficSignalSuffixed applySuffix(TrafficSignal source);

@EnumMapping defines custom mappings for enum types. nameTransformationStrategy specifies the transformation strategy to apply to the enum constant name before mapping. We pass the appropriate controlling value in the configuration.

Here is the test to check the suffix:

@ParameterizedTest
@CsvSource({"Off,Off_Value", "Go,Go_Value"})
void whenTrafficSignalIsMappedWithSuffix_thenGetTrafficSignalSuffixed(TrafficSignal source, TrafficSignalSuffixed expected) {
    TrafficSignalSuffixed result = TrafficSignalMapper.INSTANCE.applySuffix(source);
    assertEquals(expected, result);
}

5.2. Applying Prefix to Source Enum

We can also apply a prefix to the source enum to get the target enum. For instance, Go becomes Value_Go:

public enum TrafficSignalPrefixed { Value_Off, Value_Stop, Value_Go }

Let’s define the mapping:

@EnumMapping(nameTransformationStrategy = MappingConstants.PREFIX_TRANSFORMATION, configuration = "Value_")
TrafficSignalPrefixed applyPrefix(TrafficSignal source);

PREFIX_TRANSFORMATION tells MapStruct to apply the prefix “Value_” to the source enum.

Let’s check the prefix mapping:

@ParameterizedTest
@CsvSource({"Off,Value_Off", "Go,Value_Go"})
void whenTrafficSignalIsMappedWithPrefix_thenGetTrafficSignalPrefixed(TrafficSignal source, TrafficSignalPrefixed expected) {
    TrafficSignalPrefixed result = TrafficSignalMapper.INSTANCE.applyPrefix(source);
    assertEquals(expected, result);
}

5.3. Stripping Suffix From Source Enum

We remove suffixes from the source enum to get the target enum. For instance, Go_Value becomes Go.

Let’s define the mapping:

@EnumMapping(nameTransformationStrategy = MappingConstants.STRIP_SUFFIX_TRANSFORMATION, configuration = "_Value")
TrafficSignal stripSuffix(TrafficSignalSuffixed source);

STRIP_SUFFIX_TRANSFORMATION tells MapStruct to remove the suffix “_Value” from the source enum.

Here’s the test to check the stripped suffix:

@ParameterizedTest
@CsvSource({"Off_Value,Off", "Go_Value,Go"})
void whenTrafficSignalSuffixedMappedWithStripped_thenGetTrafficSignal(TrafficSignalSuffixed source, TrafficSignal expected) {
    TrafficSignal result = TrafficSignalMapper.INSTANCE.stripSuffix(source);
    assertEquals(expected, result);
}

5.4. Stripping Prefix From Source Enum

We remove a prefix from the source enum to get the target enum. For instance, Value_Go becomes Go.

Let’s define the mapping:

@EnumMapping(nameTransformationStrategy = MappingConstants.STRIP_PREFIX_TRANSFORMATION, configuration = "Value_")
TrafficSignal stripPrefix(TrafficSignalPrefixed source);

STRIP_PREFIX_TRANSFORMATION tells MapStruct to remove the prefix “Value_” from the source enum.

And here’s the test to check the stripped prefix:

@ParameterizedTest
@CsvSource({"Value_Off,Off", "Value_Stop,Stop"})
void whenTrafficSignalPrefixedMappedWithStripped_thenGetTrafficSignal(TrafficSignalPrefixed source, TrafficSignal expected) {
    TrafficSignal result = TrafficSignalMapper.INSTANCE.stripPrefix(source);
    assertEquals(expected, result);
}

5.5. Applying Lowercase to Source Enum

We apply lowercase to the source enum to get the target enum. For instance, Go becomes go:

public enum TrafficSignalLowercase { off, stop, go }

Let’s define the mapping:

@EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "lower")
TrafficSignalLowercase applyLowercase(TrafficSignal source);

CASE_TRANSFORMATION and lower configuration tells MapStruct to apply lowercase to source enum.

Here is the test method to check for lowercase mapping:

@ParameterizedTest
@CsvSource({"Off,off", "Go,go"})
void whenTrafficSignalMappedWithLower_thenGetTrafficSignalLowercase(TrafficSignal source, TrafficSignalLowercase expected) {
    TrafficSignalLowercase result = TrafficSignalMapper.INSTANCE.applyLowercase(source);
    assertEquals(expected, result);
}

5.6. Applying Uppercase to Source Enum

We apply uppercase to the source enum to get the target enum. For instance, Mon becomes MON:

public enum TrafficSignalUppercase { OFF, STOP, GO }

Let’s define the mapping:

@EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "upper")
TrafficSignalUppercase applyUppercase(TrafficSignal source);

CASE_TRANSFORMATION and upper configuration tell MapStruct to apply uppercase to the source enum.

Here is the test to verify uppercase mapping:

@ParameterizedTest
@CsvSource({"Off,OFF", "Go,GO"})
void whenTrafficSignalMappedWithUpper_thenGetTrafficSignalUppercase(TrafficSignal source, TrafficSignalUppercase expected) {
    TrafficSignalUppercase result = TrafficSignalMapper.INSTANCE.applyUppercase(source);
    assertEquals(expected, result);
}

5.7. Applying Capital Case to Source Enum

We apply the title case to the source enum to get the target enum. For instance, go becomes Go:

@EnumMapping(nameTransformationStrategy = MappingConstants.CASE_TRANSFORMATION, configuration = "captial")
TrafficSignal lowercaseToCapital(TrafficSignalLowercase source);

CASE_TRANSFORMATION and capital configuration tell MapStruct to capitalize the source enum.

Here is the test to check the capital case:

@ParameterizedTest
@CsvSource({"OFF_VALUE,Off_Value", "GO_VALUE,Go_Value"})
void whenTrafficSignalUnderscoreMappedWithCapital_thenGetStringCapital(TrafficSignalUnderscore source, String expected) {
    String result = TrafficSignalMapper.INSTANCE.underscoreToCapital(source);
    assertEquals(expected, result);
}

6. Additional Use Cases for Enum Mapping

There would be scenarios when we map the enum back to other types. Let’s look at them in this section.

6.1. Mapping Enum to String

Let’s define the mapping:

@ValueMapping(target = "Off", source = "Off")
@ValueMapping(target = "Go", source = "Go")
@ValueMapping(target = "Stop", source = "Stop")
String trafficSignalToString(TrafficSignal source);

The @ValueMapping maps enum values to strings. For instance, we map the Go enum to the “Go” string value, and so on.

Here is the test to check the string mapping:

@Test
void whenTrafficSignalIsMapped_thenGetString() {
    TrafficSignal source = TrafficSignal.Go;
    String targetTrafficSignalStr = TrafficSignalMapper.INSTANCE.trafficSignalToString(source);
    assertEquals("Go", targetTrafficSignalStr);
}

It verifies if the mapping maps enum TrafficSignal.Go to a string literal “Go”.

6.2. Mapping Enum to Integer or Other Numeric Types

Mapping directly to an integer can cause ambiguity due to multiple constructors. We add a default mapper method that converts the enum to an integer. In addition, we can also define a class with an integer property to address this issue.

Let’s define a wrapper class:

public class TrafficSignalNumber
{
    private Integer number;
    // getters and setters
}

Let’s map the enum to an integer using the default method:

@Mapping(target = "number", source = ".")
TrafficSignalNumber trafficSignalToTrafficSignalNumber(TrafficSignal source);

default Integer convertTrafficSignalToInteger(TrafficSignal source) {
    Integer result = null;
    switch (source) {
        case Off:
            result = 0;
            break;
        case Stop:
            result = 1;
            break;
        case Go:
            result = 2;
            break;
    }
    return result;
}

Here is the test to check the integer result:

@ParameterizedTest
@CsvSource({"Off,0", "Stop,1"})
void whenTrafficSignalIsMapped_thenGetInt(TrafficSignal source, int expected) {
    Integer targetTrafficSignalInt = TrafficSignalMapper.INSTANCE.convertTrafficSignalToInteger(source);
    TrafficSignalNumber targetTrafficSignalNumber = TrafficSignalMapper.INSTANCE.trafficSignalToTrafficSignalNumber(source);
    assertEquals(expected, targetTrafficSignalInt.intValue());
    assertEquals(expected, targetTrafficSignalNumber.getNumber().intValue());
}

7. Handling Unknown Enum Values

We need to handle unmatched enum values by setting defaults, handling nulls, or throwing exceptions based on the business logic. 

7.1. MapStruct Throws an Exception for Any Unmapped Properties

MapStruct raises an error if a source enum doesn’t have a corresponding one in the target type. In addition, MapStruct can also map remaining or unmapped values to a default.

We have two options applicable to source only: ANY_REMAINING and ANY_UNMAPPED. However, we need to use only one of these options at a time.

7.2. Mapping Remaining Properties

The ANY_REMAINING option maps any remaining source values with the same name to the default value.

Let’s define  a simple traffic signal:

public enum SimpleTrafficSignal { Off, On }

Notably, it has less number of values than TrafficSignal. However, MapStruct needs us to map all enum values.

Let’s define the mapping:

@ValueMapping(target = "Off", source = "Off")
@ValueMapping(target = "On", source = "Go")
@ValueMapping(target = "Off", source = "Stop")
SimpleTrafficSignal toSimpleTrafficSignal(TrafficSignal source);

We explicitly map to Off. It would be inconvenient to map these if there are many such values. We may miss mapping a few values. That is where ANY_REMAINING helps.

Let’s define the mapping:

@ValueMapping(target = "On", source = "Go")
@ValueMapping(target = "Off", source = MappingConstants.ANY_REMAINING)
SimpleTrafficSignal toSimpleTrafficSignalWithRemaining(TrafficSignal source);

Here, we map Go to On. And then using MappingConstants.ANY_REMAINING, we map any remaining value to Off. Isn’t that a cleaner implementation now?

Here is the test to check the remaining mapping:

@ParameterizedTest
@CsvSource({"Off,Off", "Go,On", "Stop,Off"})
void whenTrafficSignalIsMappedWithRemaining_thenGetTrafficSignal(TrafficSignal source, SimpleTrafficSignal expected) {
    SimpleTrafficSignal targetTrafficSignal = TrafficSignalMapper.INSTANCE.toSimpleTrafficSignalWithRemaining(source);
    assertEquals(expected, targetTrafficSignal);
}

It verifies that all other values are mapped to Off except for the value Go.

7.3. Mapping Unmapped Properties

Instead of remaining values, we can instruct MapStruct to map the unmapped values irrespective of the name.

Let’s define the mapping:

@ValueMapping(target = "On", source = "Go")
@ValueMapping(target = "Off", source = MappingConstants.ANY_UNMAPPED)
SimpleTrafficSignal toSimpleTrafficSignalWithUnmapped(TrafficSignal source);

Here is the test to check unmapped mapping:

@ParameterizedTest
@CsvSource({"Off,Off", "Go,On", "Stop,Off"})
void whenTrafficSignalIsMappedWithUnmapped_thenGetTrafficSignal(TrafficSignal source, SimpleTrafficSignal expected) {
    SimpleTrafficSignal target = TrafficSignalMapper.INSTANCE.toSimpleTrafficSignalWithUnmapped(source);
    assertEquals(expected, target);
}

It verifies that all other values are mapped to Off except for the value Go.

7.4. Handling Null Values

MapStruct can handle null sources and null targets using the NULL keyword.

Let’s suppose we need to map null input to OffGo to On, and any other unmapped value to null.

Let’s define the mapping:

@ValueMapping(target = "Off", source = MappingConstants.NULL)
@ValueMapping(target = "On", source = "Go")
@ValueMapping(target = MappingConstants.NULL, source = MappingConstants.ANY_UNMAPPED)
SimpleTrafficSignal toSimpleTrafficSignalWithNullHandling(TrafficSignal source);

We use MappingConstants.NULL to set the null value to the target. It’s also used to indicate null input.

Here is the test to check null mapping:

@CsvSource({",Off", "Go,On", "Stop,"})
void whenTrafficSignalIsMappedWithNull_thenGetTrafficSignal(TrafficSignal source, SimpleTrafficSignal expected) {
    SimpleTrafficSignal targetTrafficSignal = TrafficSignalMapper.INSTANCE.toSimpleTrafficSignalWithNullHandling(source);
    assertEquals(expected, targetTrafficSignal);
}

7.5. Raising Exceptions

Let’s consider a scenario where we raise an exception instead of mapping it to a default or null.

Let’s define the mapping:

@ValueMapping(target = "On", source = "Go")
@ValueMapping(target = MappingConstants.THROW_EXCEPTION, source = MappingConstants.ANY_UNMAPPED)
@ValueMapping(target = MappingConstants.THROW_EXCEPTION, source = MappingConstants.NULL)
SimpleTrafficSignal toSimpleTrafficSignalWithExceptionHandling(TrafficSignal source);

We use MappingConstants.THROW_EXCEPTION to raise an exception for any unmapped input.

Here is the test to check the exception thrown:

@ParameterizedTest
@CsvSource({",", "Go,On", "Stop,"})
void whenTrafficSignalIsMappedWithException_thenGetTrafficSignal(TrafficSignal source, SimpleTrafficSignal expected) {
    if (source == TrafficSignal.Go) {
        SimpleTrafficSignal targetTrafficSignal = TrafficSignalMapper.INSTANCE.toSimpleTrafficSignalWithExceptionHandling(source);
        assertEquals(expected, targetTrafficSignal);
    } else {
        Exception exception = assertThrows(IllegalArgumentException.class, () -> {
            TrafficSignalMapper.INSTANCE.toSimpleTrafficSignalWithExceptionHandling(source);
        });
        assertEquals("Unexpected enum constant: " + source, exception.getMessage());
    }
}

It verifies that the result is an exception for Stop, or else it’s an expected signal.

8. Conclusion

In this article, we learned to map between enum types and other data types like strings or integers using MapStruct @ValueMapping. Whether mapping one enum to another or gracefully handling unknown enum values, @ValueMapping offers flexibility and strength in mapping tasks. By adhering to best practices and taking care of null inputs and unmatched values, we improve code clarity and maintainability.

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.

Course – LS – NPI (cat=Java)
announcement - icon

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

>> CHECK OUT THE COURSE

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