Converter Pattern in Java 8
Our Java team has recently prepared a custom hands-on workshop on functional programming in Java 8. The participants solved our coding tasks, trying out the Java 8’s features in separation, and now it’s time we show off how we employ the full power of Java 8 in our real-life projects.
Table of contents
This is a common problem: to have to convert pairs of similar objects from one to another (in our case – domain classes to DTOs, which are then sent to frontend as JSON objects) and the other way around. In this case, what we need is a one-shot conversion, to convert just once in the object’s lifetime. What we do not want is coupling between the two kinds of objects: DTO classes shouldn’t “know” about domain classes and the other way around.
Converter class in Java
As changes in the source object do not have to affect the destination object, the ideal custom solution would be to create a single “Mapper” (or “Converter”) class for each pair. There are also tools which are able to map fields of similar classes, based on field names. But how about “producing” whole collections of our source object? How to produce collections of destination objects, without too much boilerplate code?
What addresses our needs, is Java 8 and its three core features:
- default method implementation in interfaces,
- stream,
- lambdas (here in the form of a method reference)
Default method implementation is what saves us from boilerplate code, creating collections of objects. Streams and lambdas build a beautiful code transforming our collections. Let’s take a look at the final class hierarchy and the code itself:
public interface GenericConverter {
E createFrom(D dto);
D createFrom(E entity);
E updateEntity(E entity, D dto);
default List createFromEntities(final Collection entities) {
return entities.stream()
.map(this::createFrom)
.collect(Collectors.toList());
}
default List createFromDtos(final Collection dtos) {
return dtos.stream()
.map(this::createFrom)
.collect(Collectors.toList());
}
}
How to create a Converter pattern in Java?
Having implemented the default method to convert a collection of data transfer objects (D) into entities (E), as well as another one that does the opposite, we don’t need to implement this in concrete implementations of the converter anymore. Creating a converter for a single DTO/domain class is as simple as that:
@Component
public class AccountConverterImpl implements AccountConverter {
`@Override`
`public Account createFrom(final AccountDto dto) {`
`return updateEntity(new Account(), dto);`
`}`
@Override
public AccountDto createFrom(final Account entity) {
AccountDto accountDto = new AccountDto();
accountDto.setAccountType(entity.getAccountType());
accountDto.setActive(entity.getActive());
accountDto.setEmail(entity.getUserId());
ClassUtils.setIfNotNull(
entity::getPassword, accountDto::setPassword);
return accountDto;
}
@Override
public Account updateEntity(final Account entity,
final AccountDto dto) {
entity.setUserId(dto.getEmail());
entity.setActive(dto.getActive());
ClassUtils.setIfNotNull(
dto::getAccountType, entity::setAccountType);
return entity;
}
}
Another custom feature you can see there is our ClassUtils.setIfNotNull method, which only calls the setter if the getter yields a non-null value:
public class ClassUtils {
protected ClassUtils() { }
public static void setIfNotNull(final Supplier getter, final Consumer setter) {
T t = getter.get();
if (null != t) {
setter.accept(t);
}
}
}
Java Converter pattern - key takeaways
So there we have a complete Converter pattern using all the Java 8 goodness. Adding a new Converter for another entity-DTO pair (like User, Address, etc.) just requires the creation of a new UserConverterImpl class, implementing its own UserConverter, which in turn should implement GenericConverter. This way, you will be able to convert collections of objects out-of-the box. This is possible thanks to the default method implementations in GenericConverter interface, which is a very handy Java 8’s feature.
Share this article: