33

There is a List A with property Developer. Developer schema likes that:

@Getter
@Setter
public class Developer {
    private String name;
    private int age;

    public Developer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Developer name(String name) {
        this.name = name;
        return this;
    }

    public Developer name(int age) {
        this.age = age;
        return this;
    }
}

List A with properties:

List<Developer> A = ImmutableList.of(new Developer("Ivan", 25), new Developer("Curry", 28));

It is demanded to convert List A to List B which with property ProductManager and the properties is same as the ones of List A.

@Getter
@Setter
public class ProductManager {
    private String name;
    private int age;

    public ProductManager(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public ProductManager name(String name) {
        this.name = name;
        return this;
    }

    public ProductManager name(int age) {
        this.age = age;
        return this;
    }
}

In the old days, we would write codes like:

public List<ProductManager> convert() {
    List<ProductManager> pros = new ArrayList<>();
    for (Developer dev: A) {
        ProductManager manager = new ProductManager();
        manager.setName(dev.getName());
        manager.setAge(dev.getAge());
        pros.add(manager);
    }
    return pros;
}

How could we write the above in a more elegant and brief manner with Java 8?

Ivan
  • 661
  • 1
  • 8
  • 15
  • 3
    The real question here is why `Developer` and `ProductManager` don't seem to inherit from a common superclass `Person` that encapsulates the `name` and `age`. Before you go upgrading to nifty Java8 bells & whistles, you should probably get the design right. – Jim Garrison Oct 14 '16 at 04:52
  • @JimGarrison Yes, I agree with you, I need to pay more attention to fundamentals. If there are more properties, maybe more than 20, and the constructor can not use directly, how to convert ? – Ivan Oct 14 '16 at 05:48
  • 1
    If `Developer` and `ProductManager` had a common base class, `Person`, defining all common properties, there was no problem defining a constructor with a `Person` parameter in both classes. These constructors would just delegate to the constructor of the `Person` base class which does the work, so it has to be implemented only once. Note that this is an established pattern, i.e. all standard `Collection` types provide a constructor accepting a `Collection` argument to copy from. – Holger Oct 14 '16 at 08:17

5 Answers5

92

you will have to use something like below :

List<ProductManager> B = A.stream()
        .map(developer -> new ProductManager(developer.getName(), developer.getAge()))
        .collect(Collectors.toList());

// for large # of properties assuming the attributes have similar names //other wise with different names refer this

List<ProductManager> B = A.stream().map(developer -> {
            ProductManager productManager = new ProductManager();
            try {
                PropertyUtils.copyProperties(productManager, developer);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return productManager;
        }).collect(Collectors.toList());

        B.forEach(System.out::println);
Ramachandran.A.G
  • 4,788
  • 1
  • 12
  • 24
  • If there are more properties, maybe more than 20, and the constructor can not use directly, how to convert ? – Ivan Oct 14 '16 at 05:49
  • 1
    @Ivan added a sample with PropertyUtils , seems to be a easy way to do it – Ramachandran.A.G Oct 14 '16 at 06:50
  • @Ivan use [Map Struct Java Bean mapping annotation processor](https://www.youtube.com/watch?v=nvjqtWQ5zj8) to convert Bean to DTO / JSON object easily if the entity contains more than 5 or 20 properties. By using [Lombok](https://www.youtube.com/watch?v=tXg9miX0pX0) we can avoid getters and setters in bean / Entity classes. Read this [Guide to MapStruct in Java - Advanced Mapping Library](https://stackabuse.com/guide-to-mapstruct-in-java-advanced-mapping-library/) article also to get idea on MapStruct. – UdayKiran Pulipati Sep 27 '20 at 03:21
8

Probably, like this:

List<ProductManager> B = A.stream()
        .map(developer -> new ProductManager(developer.name, developer.age))
        .collect(Collectors.toList());
Slava
  • 827
  • 5
  • 13
5

If there are more properties, maybe more than 20, and the constructor can not use directly, how to convert ?

If you have any more than 3-4 properties to set, you should use a Builder, like so:

List<Developer> A = ImmutableList.of(new Developer("Ivan", 25),
                                     new Developer("Curry", 28));

Function<Developer, ProductManager> converter = dev -> new ProductManagerBuilder()
        .setAge(dev.getAge())
        .setName(dev.getName())
        .setProperty1(dev.getProperty1())
        .setProperty2(dev.getProperty2())
        ...
        .build();

List<ProductManager> productManagers = A.stream()
                                        .map(converter)
                                        .collect(toList());
Mahesh
  • 5,158
  • 2
  • 15
  • 20
  • You mean to use build pattern? It is right. My question then was how to write function body in inner map, instead of constructor. Builder pattern works like constructor in this sample, so it is not my intention, thanks anyway. – Ivan Oct 19 '16 at 10:26
4

Java 16 has updated the collection api to .toList() instead of .collect(Collectors.toList())

List<ProductManager> B = A.stream()
        .map(developer -> new ProductManager(developer.name, developer.age))
        .toList();
San Jaisy
  • 15,327
  • 34
  • 171
  • 290
1

If you are fine to add belwo constructor in ProductManger

public ProductManager(Developer d) {
    this.name = d.getName();
    this.age = d.getAge();
}

then to convert using constructor reference

    List<Developer> developers = Arrays.asList(new Developer("abc", 25));
    List<ProductManager> managers = developers.stream().map(ProductManager::new).collect(Collectors.toList());
    System.out.println(managers);

otherwise you can provide custom mapper

Function<Developer, ProductManager> mapper = d -> new ProductManager(d.getName(), d.getAge()); 

use this in map function

Output

[ProductManager [name=abc, age=25]]
Saravana
  • 12,647
  • 2
  • 39
  • 57
  • If there are more properties, maybe more than 20, and the constructor can not use directly, how to convert ? – Ivan Oct 14 '16 at 05:45
  • In the above mapper function instead of using constructor, create a product manager object, set all it's properties and return it – Saravana Oct 14 '16 at 05:55