Convert DTO to Domain models and back with lambda

Viewed 17535

I am new in Java so I want ask you if exist some pretty solution by lambda which convert One type object to another?

I had function in service:

public List<MyObjectDto> findAll() {
    List<MyObject> list = repository.findAll();
    return list.someMagicToConvertToDtoObjectsInList();
}

Any ideas for that?

2 Answers

There's no magic here but you can stream the list and then implement a map function to transform from the domain representation to the DTO representation.

For example, given a domain object with id, firstName, lastName and a DTO with id, name (where name is a concatenation of firstName and lastName) the following code ...

List<MyObject> domain = new ArrayList<>();
domain.add(new MyObject(1, "John", "Smith"));
domain.add(new MyObject(1, "Bob", "Bailey"));

// using the verbose statement of function (rahter than a lambda) 
// to make it easier to see how the map function works
List<MyDto> asDto = domain.stream().map(new Function<MyObject, MyDto>() {
    @Override
    public MyDto apply(MyObject s) {
        // a simple mapping from domain to dto
        return new MyDto(s.getId(), s.getFirstName() + " " + s.getLastName());
    }
}).collect(Collectors.toList());

System.out.println(asDto);

... prints out:

[
    MyDto{id=1, name='John Smith'}, 
    MyDto{id=1, name='Bob Bailey'}
]

Of course, the above use of an anonymous class looks somewhat out of place when using stream() so here's the same code expressed using a lambda:

List<MyDto> asDto = domain.stream().map(
        s -> new MyDto(s.getId(), s.getFirstName() + " " + s.getLastName())
).collect(Collectors.toList());

By using streams, you can use the following:

public List<MyObjectDTO> findAll() {
    List<MyObject> list = repository.findAll();
    return list.stream()
            .map(MyObjectDTO::new)
            .collect(Collectors.toCollection(ArrayList::new));
}

For this to work, your DTO class will need to have a constructor which accepts your domain model:

public class MyObjectDTO {

    // fields

    public MyObjectDTO(MyObject myObject) {
        // map MyObject fields to MyObjectDTO fields
    }
}

EDIT

An alternative to the parametric constructor is encapsulating the conversion in a method, for example:

public class Service {    

    public List<MyObjectDto> findAll() {
        List<MyObject> list = repository.findAll();
        return list.stream()
                .map(Service::convert)
                .collect(Collectors.toCollection(ArrayList::new));
    }

    private MyObjectDTO convert(MyObject myObject) {
        // conversion here
    }
}
Related