'Mapping Hierarchy of Classes with Mapstruct

I have a hierarchy of classes: VehicleDTO is a base abstract class. CarDTO, TruckDTO, VanDTO extend from it.

I have the same hierarchy on the other side of a mapper: VehicleBO <- CarBO, TruckBO, VanBO.

I want to have all the mapping logic consolidated in one mapper. Period.

I have defined mappings for common attributes, but here is when it becomes interesting, I get this exception during compilation:

The return type ... is an abstract class or interface.
Provide a non abstract / non interface result type or a factory method. 

So, how do I specify a factory method, that based on a value of a particular attribute or a class of the pojo, would create a target object for me? I would appreciate a good code snippet that actually does the trick.

Thanks!



Solution 1:[1]

You can use a method annotated with @ObjectFactory receiving a source parameter for what you need.

Let's assume that you have a mapper that looks like:

@Mapper
public interface VehicleMapper {

    VehicleDTO map(VehicleBO vehicle);

    // more
}

If you add a method looking like:

@ObjectFactory
default VehicleDTO createVehicleDto(VehicleBO vehicle) {
    // your creation logic
}

Then MapStruct will use the createVehicleDto to create the VehicleDTO object.

NOTE when mapping hierarchies and when the mapping looks like the one in the answer then MapStruct will only map the properties which are in the VehicleDTO class and not in possible implementations of the class. The reason for that is that MapStruct generates the mapping code during compilation and not during runtime.

For mapping hierarchies like what you explained you can do something like the following:

public interface VehicleMapper {

    default VehicleDTO map(VehicleBO vehicle) {
        if (vehicle instanceOf CarBO) {
            return map((CarBO) vehicle);
        } else if (vehicle instanceOf TruckBO) {
            return map((TruckBO) vehicle);
        } else if (vehicle instanceOf VanBO) {
            return map((VanBO) vehicle);
        } else {
            //TODO decide what you want to do
        }
    }

    @Named("car")
    CarDTO map(CarBO car);

    @Named("truck")
    TruckDTO map(TruckBO truck);

    @Named("car")
    VanDTO map(VanBO van);

    // more
}

There is mapstruct/mapstruct#131 requesting for generating code like my example out of the box

Solution 2:[2]

Nowadays, maybe using Visitor pattern could be better choice instead of the instanceOf way, check below:

https://techlab.bol.com/en/blog/mapstruct-object-hierarchies

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 SebastianX