0

i have a domain class(DB):

public class PersonDoamin {
private String name;
private String age;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getAge() {
    return age;
}

public void setAge(String age) {
    this.age = age;
}
}

i also have model class:

public class PersonBean extends PersonDoamin {

}

so when i go to DAOImpl class and query for List and transfer this list to List and return to users as i have interface method for List getAllPerson(). so my questions is here when i transfer all data from List. Here i have some utility method that copies from one bean to another like this:

List<PersonDoamin> list = PersonDAO.getAllPersons();
List<PersonBean> pbList = new ArrayList<PersonBean>();

/* this below logic is pretty much in the all DAO impl*/

for(PersonDoamin p : list){
PersonBean pb = new PersonBean();
CopyHelper.copyBean(p, pb);
pbList.add(pb);
}
return pbList;

can we replace the looping and copying and adding to another list and returning part with somekind of generic method which will take any object two list and loop thorugh one and add it to another passed List parameter and return it. something like below which is not perfect right now:

public static <T> List<T> listToArray(List<T> list,List<T> list2) {

    for(T element : list){
        list2.add(element);
    }
    return list2;
}

public static void main(String[] args) {
    List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
    PersonDoamin p = new PersonDoamin();
    p.setName("aj");
    p.setAge("25");
    personList.add(p);
    List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
    Test.listToArray(personList , personBeansToReturn );

}
userDT
  • 76
  • 1
  • 2
  • 9
  • PECS might help:http://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super – markspace Sep 15 '16 at 02:14
  • 1
    One problem I think I see is that you're trying to put `PersonDomain` into a `PersonBean` list, and that can't work. – markspace Sep 15 '16 at 02:16
  • What you wrote in the `listToArray` does not align with what you are doing in the original loop. You are not transforming the domain to bean – Adrian Shum Sep 15 '16 at 02:35
  • Oh, maybe that's the question. Is the OP looking for a way to call `new T` on a generic type? – markspace Sep 15 '16 at 02:36

4 Answers4

2

A bit off topic, your design seems a bit weird that you have "Domain" class and "Bean" class and have "Bean" extends "Domain"...

Anyway, come back to your question, what you are trying to do is:

  1. You have a List<Domain>
  2. You want to transform each Domain in the List into a Bean (by use of some util method)
  3. Put the resulting Beans into a list and return

Let's go through it step by step.

(by the way, the listToArray method you wrote does not align with your original loop as it does not do the transformation (point 2). I guess it is typo?)

(all psuedo code as I don't have environment on hand to make it compile. Concept should be correct I guess)


Step 1: Util method for Person

One biggest problem of your original util method is that, it is illegal to put a Parent object instance to a List of Child (it should be easy to figure why by yourself).

The util method should look like this:

List<PersonBean> toBeans(List<PersonDomain> domains) {
    List<PersonBean> beans = new ArrayList<>(domains.size());
    for (PersonDomain domain: domains) {
        PersonBean bean = new PersonBean(); 
        CopyHelper.copyBean(domain, bean);
        beans.add(bean);
    }
    return beans;
}

Step 2: Make it generic

The problem above is that it only works for Person. If you want to make it generic, you will also need to provide the function to transform Domain to Bean:

(Assume you are using Java8, should be trivial to make your own interface if you are using older version)

<D,B> List<B> toBeans(List<D> domains, Function<B,D> mapper) {
    List<PersonBean> beans = new ArrayList<>(domains.size());
    for (PersonDomain domain: domains) {
        beans.add(mapper.apply(domain));
    }
    return beans;
}

so that you can use it by:

return toBeans(personDomains, (domain) -> {
                         PersonBean bean = new PersonBean(); 
                         CopyHelper.copyBean(domain, bean);
                         return bean;
                     });

(You may consider wrap the function if in most case you are going to use the CopyHelper way)

<D,B> List<B> toBeansByBeanCopy(List<D> domains, Class<B> beanClass) {
    return toBeans(domains, (domain)-> {
                         B bean = beanClass.newInstance(); 
                         CopyHelper.copyBean(domain, bean);
                         return bean;
                     });
}

so that you can use it as

return toBeansByBeanCopy(personDomains, PersonBean.class);

Step 3: Java has done it for you

Actually what you are trying to do above, it is already provided by Java in Java 8. You can simply do:

return personDomains.stream()
    .map(d -> {
        PersonBean bean = new PersonBean(); 
        CopyHelper.copyBean(domain, bean);
        return bean;
        })
    .collect(Collectors.toList());

You may write a little method to use in the lambda expression if it is the standard way.

return personDomains.stream()
    .map(BeanMapper.mapper(PersonBean.class))
    .collect(Collectors.toList());

(Leave the implementation as your exercise)

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • The technology i am using is JSF 2.0, primefaces , spring 3.2.9 with ibatis and domain class is auto generated with ibatis tool. so instead of defining bean with same properties that domain class has i decided to extend it so that there wont be any duplicate properties with same name. And CopyHelper.copyBean method is using BeanUtils.copyProperties method of spring bean util to copy same properties from one bean to another. – userDT Sep 15 '16 at 14:20
  • here i just want to avoid typing same code where i loop through list of domain classes that i got from calling database and put it on list of bean that extends domain bean and return it to controller.: – userDT Sep 15 '16 at 14:22
  • I still don't think your choice of domain/bean design is rational. Anyway, it is off topic :) what you are looking for should have been nicely solved by what I suggested here. – Adrian Shum Sep 15 '16 at 15:31
  • For your case, you should simply create the "to list" in the function – Adrian Shum Sep 28 '16 at 14:16
1

If you're looking for a way to call new on a generic type, you can, sort of. You have to use reflection and call newInstance on the Class object. I don't know if this is going to be feasible for you.

Also, I don't see anyway of realistically implementing your bean copy method without using some heavy reflection as well. In the example below I faked by just casting to the required classes.

public class GenericCopyTest
{

   public static void main( String[] args ) throws Exception
   {
      List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
      PersonDoamin p = new PersonDoamin();
      p.setName( "aj" );
      p.setAge( "25" );
      personList.add( p );
      List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
      copyAndDowncast( personList, personBeansToReturn, PersonBean.class );
      System.out.println( personBeansToReturn );
   }

   public static <T,U extends T> List<U> copyAndDowncast( List<T> from, 
           List<U> to, Class<U> type ) 
           throws InstantiationException, IllegalAccessException
   {
      for( T element : from ) {
         U nu = type.newInstance();
         copyBean( element, nu );
         to.add( nu );
      }
      return to;
   }

   private static <X,Y extends X> void copyBean( X from, Y nu ) {
      ((PersonBean)nu).setName( ((PersonDoamin)from).getName() );
      ((PersonBean)nu).setAge( ((PersonDoamin)from).getAge() );
   }

}

class PersonDoamin {
   private String name;
   private String age;

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getAge() {
       return age;
   }

   public void setAge(String age) {
       this.age = age;
   }

   @Override
   public String toString()
   {
      return "PersonDoamin{" + "name=" + name + ", age=" + age + '}';
   }

}
class PersonBean extends PersonDoamin {

   @Override
   public String toString()
   {
      return "PersonBean{" + getName() + ',' + getAge()+ '}';
   }

}

Output:

run:
[PersonBean{aj,25}]
BUILD SUCCESSFUL (total time: 0 seconds)
markspace
  • 10,621
  • 3
  • 25
  • 39
  • copyBean method uses Beanutils.copyProperties(source, destination) method of spring BeanUtils class to copy same properties from one bean to another bean. – userDT Sep 15 '16 at 14:35
  • based on above answers i managed to get it work. public static List getNewList(List from, List to, Class type) throws InstantiationException, IllegalAccessException { for (T element : from) { U dt = type.newInstance(); BeanUtils.copyProperties(element, dt); to.add(dt); } return to; } – userDT Oct 11 '16 at 13:44
0

Why not just use addAll() for this? It does what you're trying to do, and it's already part of the system library.

Remember you can add a PersonBean to a PersonDomain list, but not the other way around.

public class GenericCopyTest
{
   public static void main( String[] args ) {
      List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
      List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
      personList.addAll( personBeansToReturn );
      personBeansToReturn.addAll( personList );  // <-- FAILS
                                                 // No suitable method found
   }
}

class PersonDoamin {}
class PersonBean extends PersonDoamin {}
markspace
  • 10,621
  • 3
  • 25
  • 39
0

If you want to put more than one bean class in the same list, how about creating the list with parent class PersonDoamin , and then, you can store both PersonDoamin and PersonBean classes.

public static void main(String[] args) {
    List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
    PersonDoamin p = new PersonDoamin();
    p.setName("aj");
    p.setAge("25");
    personList.add(p);

    // Changed here. PersonBean => PersonDoamin
    List<PersonDoamin> personBeansToReturn = new ArrayList<PersonDoamin>();

    Test.listToArray(personList, personBeansToReturn);

    // also you can insert PersonBean into the list
    personBeansToReturn.add(new PersonBean());

}
riversun
  • 758
  • 8
  • 12