15

I have this question. But it will be difficult for me to explain as I don't know exact terms to use. Hope someone will understand. I'll try to discribe to the best i can. I feel like this is much related to parsing

Say there are two classes. And in both classes I have some variables, say strings (just for simplicity, variable type can be any), which have similar names.

Eg:
    class ClassA{
        String x,y,z;
    }

    class ClassB{
        String x,y,z;
    }

Now, what i need is, i need to copy the value of one class's variable values to other classes corresponding variable.

Eg:
    ClassA aa=new ClassA();
    ClassB bb=new ClassB();
    //set bb's variables
    aa.x=bb.x;
    aa.y=bb.y;
    aa.z=bb.z;

like that.

But please note that what i need is not the above method. I hope there will be a way to write a simple method, so that it will identify the relevent variable by the name passed to it. Then it will do the value assignment accordingly.

My imagined method is like this,

void assign(String val){        
    // aa.<val>=val
}

For example if you pass bb.x to assign(...) method, then it will do aa.x=bb.x assignment.

Hope this is clear enough. There must be a better way to explain this. If someone know it please edit the post(+title) to make it more clear (But save my idea)..

Please let me know if there's a way to achieve this.

Thanks!

Anubis
  • 6,995
  • 14
  • 56
  • 87
  • Do you really need a two class for that? Couldn't be just two instances of the same class? Then you can use just clone() method. – visionary Aug 10 '12 at 11:16
  • You can try a Map or a hashmap... or do something with reflection. This sort of thing has to be purpose-built so why not share a common use-case with us? – Shark Aug 10 '12 at 11:16
  • @visionary Nope. Can't go for cloning. Got two classes. :( – Anubis Aug 10 '12 at 11:25
  • @Shark What did you mean by `why not share a common use-case with us`? (I saw you changing your answer into a comment :p) – Anubis Aug 10 '12 at 11:48
  • What I mean is "give us a better idea where and how this might be used since you might have the wrong idea for solving the problem to begin with.". ClassA, ClassBB is too vague and really doesn't describe anything; sharing real class names might give insight into what they should be doing. – Shark Aug 10 '12 at 12:00
  • Or you could try serializing/deserializing thru XML, that always work. – Shark Aug 10 '12 at 12:01
  • 1
    @Shark well i'm question seeks a solution like Jean-RémyRevy & SeanPatrickFloyd suggested below. Don't get confused with class names. It's the best I can do to explain. It will be complicated if i try to give more details. – Anubis Aug 10 '12 at 12:12
  • @Shark Simply I need a method that can `identify the passed variables name` and deal with a variable `with the same name` in some other independent object (both variables will be of the same type). – Anubis Aug 10 '12 at 12:13
  • Here's an honest idea - i'd try dozer, then XML serialization, then reflection... With reflection, you could write a method to compare members of both classes and assign runtime value of one to the other. – Shark Aug 10 '12 at 12:29
  • 2
    Thanks @Shark it would be appreciated if you can explain a bit about `reflection`. I haven't come across that.. (You'd better put it as an answer, as this doesn't like extended discussions :D) – Anubis Aug 10 '12 at 13:34
  • The thing is, I never extensively used reflection... that's why I'd leave it for last :) I'll come up with a brief answer now. – Shark Aug 10 '12 at 13:54

4 Answers4

15

Dozer is fine, see Jean-Remy's answer.

Also, if the variables have getters and setters according to the JavaBeans standard, there are a number of technologies that could help you, e.g. Apache Commons / BeanUtils

Sample code (not tested):

final Map<String, Object> aProps = BeanUtils.describe(a);
final Map<String, Object> bProps = BeanUtils.describe(b);
aProps.keySet().retainAll(bProps.keySet());
for (Entry<String, Object> entry : aProps.entrySet()) {
    BeanUtils.setProperty(b,entry.getKey(), entry.getValue());
}

Update:

If you don't have getters and setters, here's a quick hack that copies field values from one class to another as long as the fields have common names and types. I haven't tested it, but it should be OK as a starting point:

public final class Copier {

    public static void copy(final Object from, final Object to) {
        Map<String, Field> fromFields = analyze(from);
        Map<String, Field> toFields = analyze(to);
        fromFields.keySet().retainAll(toFields.keySet());
        for (Entry<String, Field> fromFieldEntry : fromFields.entrySet()) {
            final String name = fromFieldEntry.getKey();
            final Field sourceField = fromFieldEntry.getValue();
            final Field targetField = toFields.get(name);
            if (targetField.getType().isAssignableFrom(sourceField.getType())) {
                sourceField.setAccessible(true);
                if (Modifier.isFinal(targetField.getModifiers())) continue;
                targetField.setAccessible(true);
                try {
                    targetField.set(to, sourceField.get(from));
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException("Can't access field!");
                }
            }
        }
    }

    private static Map<String, Field> analyze(Object object) {
        if (object == null) throw new NullPointerException();

        Map<String, Field> map = new TreeMap<String, Field>();

        Class<?> current = object.getClass();
        while (current != Object.class) {
            for (Field field : current.getDeclaredFields()) {
                if (!Modifier.isStatic(field.getModifiers())) {
                    if (!map.containsKey(field.getName())) {
                        map.put(field.getName(), field);
                    }
                }
            }
            current = current.getSuperclass();
        }
        return map;
    }
}

Call Syntax:

Copier.copy(sourceObject, targetObject);
amit kumar
  • 20,438
  • 23
  • 90
  • 126
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • Thanks @SeanPatrickFloyd . Yeah, if i had `getters` and `setters` this will come in handy. Well, i'll consider that. But i'd like to experiment with that `dozer` thing now.. – Anubis Aug 10 '12 at 11:51
  • 1
    @Anubis I've added a solution for fields only – Sean Patrick Floyd Aug 10 '12 at 14:16
  • @Shark yeah, but note: it's 10 minutes worth of coding. Don't use this in production – Sean Patrick Floyd Aug 10 '12 at 15:50
  • 1
    I meant it more as proof of concept of what i suggested below. But hey, if it works... :) – Shark Aug 10 '12 at 16:58
  • 2
    works great! it is missing a "current = current.getSuperclass();" at the end of the "while (current != Object.class)" loop. Without this, it goes into an infinite loop. – jett May 01 '15 at 15:43
  • @jett First thing I hit was the endless loop, thanks for your fix! Kudos to Sean Patrick Floyd for the algorithm. – Moshe Rubin Jul 05 '16 at 03:00
  • I like this, but can you make one that returns a whole new object instead of asking you to pass one into the second parameter? – slashdottir Dec 05 '17 at 23:07
  • @slashdottir that's a lot harder to do in a type safe language. it would require creating the new class on the fly. It can be done with tools like CGLib or ByteBuddy, but it's far from trivial. And it wouldn't do you any good, either, as your client code wouldn't know the class. Better to just return a map in that case. – Sean Patrick Floyd Dec 07 '17 at 00:01
  • Update (May 2023): BeanUtils.copyProperties(sourceObject, targetObject) should work fine. – AliReza May 11 '23 at 19:38
8

Did you ever heared about Dozer ? : http://dozer.sourceforge.net/

Dozer

Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another. Typically, these Java Beans will be of different complex types.

Dozer supports simple property mapping, complex type mapping, bi-directional mapping, implicit-explicit mapping, as well as recursive mapping. This includes mapping collection attributes that also need mapping at the element level.

Dozer allow you to map Java Beans :

  • using their names (implicit mapping), i.e mapping ClassA.x to ClassB.x
  • providing hability to map diffrent structures (explicit mapping) with different names with (quite simple) xml or annoation configuration .

Here a XML example on the library site :

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">
          
  <mapping> 
    <class-a>org.dozer.vo.TestObject</class-a>
    <class-b>org.dozer.vo.TestObjectPrime</class-b>   
    <field>
      <a>one</a>
      <b>onePrime</b>
    </field>
  </mapping>  

      <!-- SNIP ... -->

</mappings>

This will map object org.dozer.vo.TestObject into TestObjectPrime, mapping all variable that are identicals together (like in your case) and variables TestObjectFoo.oneFoo into TestObjectFooPrime.oneFooPrime.

Great, isn't it ?

Community
  • 1
  • 1
Jean-Rémy Revy
  • 5,607
  • 3
  • 39
  • 65
  • appreciate your answer. yeah, i haven't ever heard about `dozer`. Seems great. I'll try that one and give a feedback. Thanks! – Anubis Aug 10 '12 at 11:46
5

Look here. Just use BeanUtils.copyProperties(newObject, oldObject);

Community
  • 1
  • 1
Dmitry
  • 727
  • 1
  • 8
  • 34
3

new answer.

I'd suggest looking into Dover as it seems pretty straightforward.

The second option is serializing classes into XML and deserializing into your target class only on members that match.

The third option I mentioned in the comment was using reflection - http://java.sun.com/developer/technicalArticles/ALT/Reflection/

This technique allows for a nice design pattern called Introspection - Java introspection and reflection which in turn allows you to discover members of a certain class...

Now, having said that, one would simply "discover" members of ClassA, fill a ArrayList with their names, discover members of ClassB, fill another ArrayList with their names, and copy values of the intersecting set. At least that's my idea on it.

Community
  • 1
  • 1
Shark
  • 6,513
  • 3
  • 28
  • 50