46

Newbie Java question. Say I have:

public class Car{
  ...
}

public class Truck extends Car{
  ...
}

Suppose I already have a Car object, how do I create a new Truck object from this Car object, so that all the values of the Car object is copied into my new Truck object? Ideally I could do something like this:

Car c = new Car();
/* ... c gets populated */

Truck t = new Truck(c);
/* would like t to have all of c's values */

Would I have to write my own copy constructor? This would have to be updated everytime Car gets a new field...

11 Answers11

41

Yes, just add a constructor to Truck. You will probably want to add a constructor to Car also, though not necessarily public:

public class Car {
    protected Car(Car orig) {
    ...
}

public class Truck extends Car {
    public Truck(Car orig) {
        super(orig);
    }
    ...
}

As a rule it's generally best to make classes either leaf (and you might want to mark those final) or abstract.

It looks as if you want a Car object, and then have the same instance turn into a Truck. A better way of doing this is to delegate behaviour to another object within Car (Vehicle). So:

public final class Vehicle {
    private VehicleBehaviour behaviour = VehicleBehaviour.CAR;

    public void becomeTruck() {
        this.behaviour =  VehicleBehaviour.TRUCK;
    } 
    ...
}

If you implement Cloneable then you can "automatically" copy an object to a instance of the same class. However there are a number of problems with that, including having to copy each field of mutable objects which is error-prone and prohibits the use of final.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • Implementing `Clonable` is [probably a bad idea](https://stackoverflow.com/questions/4081858/about-java-cloneable#4081895). – jpaugh Oct 14 '16 at 15:55
  • @jpaugh your link is six years old... are you sure `Clonable` have not improved in the meantime? – Mindwin Remember Monica Jan 05 '17 at 15:35
  • @Mindwin I suspect there are good alternatives to Clonable now, yes. The things about Clonable which make it bad is part of the API. Can't be changed without breaking backward compatibility. I found various references which all said the same thing as the above, with various dates. – jpaugh Jan 07 '17 at 00:51
  • @jpaugh so it is like that stringy part of the steak the kid pushes to the corner of the plate because he doesn't want to deal with it. – Mindwin Remember Monica Jan 07 '17 at 09:53
  • @Mindwin Clonable might be more like the steak my Mom always made: burnt crispy --- nothing stringy about it! FWIW, my memory on this subject has not been refreshed since October. – jpaugh Jan 09 '17 at 16:55
9

If you are using Spring in your project you may use ReflectionUtils.

Pawel.Duleba
  • 91
  • 1
  • 1
4

Would I have to write my own copy constructor? This would have to be updated everytime Car gets a new field...

Not at all!

Try this way:

public class Car{
    ...
}

public class Truck extends Car{
    ...

    public Truck(Car car){
        copyFields(car, this);
    }
}


public static void copyFields(Object source, Object target) {
        Field[] fieldsSource = source.getClass().getFields();
        Field[] fieldsTarget = target.getClass().getFields();

        for (Field fieldTarget : fieldsTarget)
        {
            for (Field fieldSource : fieldsSource)
            {
                if (fieldTarget.getName().equals(fieldSource.getName()))
                {
                    try
                    {
                        fieldTarget.set(target, fieldSource.get(source));
                    }
                    catch (SecurityException e)
                    {
                    }
                    catch (IllegalArgumentException e)
                    {
                    }
                    catch (IllegalAccessException e)
                    {
                    }
                    break;
                }
            }
        }
    }
Christian
  • 7,062
  • 9
  • 53
  • 79
  • 1
    Since the OP wanted to copy ALL fields, two changes should be made to your answer: use getDeclaredFields instead of getFields, and access the fields of the Parent class, not the class itself: `Field[] fieldsTarget = target.getClass().getSuperclass().getDeclaredFields();` – cabad May 23 '16 at 18:07
4

Yes, you have to do this manually. You'll also need to decide how "deeply" to copy things. For instance, suppose the Car has a collection of tyres - you could do a shallow copy of the collection (such that if the original object changes the contents of its collection, the new object would see the change too) or you could do a deep copy which created a new collection.

(This is where immutable types like String often come in handy - there's no need to clone them; you can just copy the reference and know that the contents of the object won't change.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
3

you can use reflection i do it and work fine for me:

public Child(Parent parent){
    for (Method getMethod : parent.getClass().getMethods()) {
        if (getMethod.getName().startsWith("get")) {
            try {
                Method setMethod = this.getClass().getMethod(getMethod.getName().replace("get", "set"), getMethod.getReturnType());
                setMethod.invoke(this, getMethod.invoke(parent, (Object[]) null));

            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                //not found set
            }
        }
    }
 }
rcorbellini
  • 1,307
  • 1
  • 21
  • 42
2

Would I have to write my own copy constructor? This would have to be updated everytime Car gets a new field...

Essentially, yes - you can't just convert an object in Java.

Fortunately you don't have to write all the code yourself - look into commons-beanutils, specifically methods like cloneBean. This has the added advantage that you don't have to update it every time it gets a new field!

1

You could always use a mapping Framework such as Dozer. By default (without further configuration), it maps all fields of the same name from one object to another using the getter and setter methods.

Dependency:

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.5.1</version>
</dependency>

Code:

import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;

// ...

Car c = new Car();
/* ... c gets populated */

Truck t = new Truck();
Mapper mapper = new DozerBeanMapper();
mapper.map(c, t);
/* would like t to have all of c's values */
schnatterer
  • 7,525
  • 7
  • 61
  • 80
0

The solutions presented above have limitations you should be aware of. Here's a short summary of algorithms for copying fields from one class to another.

  • Tom Hawtin: Use this if your superclass has a copy constructor. If it does not you will need a different solution.
  • Christian: Use this if the superclass does not extend any other class. This method does not copy fields recursively upwards.
  • Sean Patrick Floyd: This is a generic solution for copying all fields recursively upwards. Be sure to read @jett's comment that a single line must be added to prevent an endless loop.

I reproduce Sean Patrick Floyd's analyze function with the missing statement:

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) {
        Field[] declaredFields = current.getDeclaredFields();
        for (Field field : declaredFields) {
            if (!Modifier.isStatic(field.getModifiers())) {
                if (!map.containsKey(field.getName())) {
                    map.put(field.getName(), field);
                }
            }
        }

        current = current.getSuperclass();   /* The missing statement */
    }
    return map;
}
Community
  • 1
  • 1
Moshe Rubin
  • 1,944
  • 1
  • 17
  • 37
0

I know this is an OLD question, but I hate to leave out dated answers when things have improved.

Using JSON is much easier. Convert it to JSON and back again as child.

Here is an Android Kotlin Example.

    val gson = Gson()    
    val childClass = gson.fromJson(
        gson.toJson(parentObject), 
        object: TypeToken<ChildObject>(){}.type
    ) as ChildObject

I think in Java it would be basically.

Gson gson = new Gson()
ChildObject child = (ChildObject) gson.fromJson(
    gson.toJson(parentObject),
    TypeToken<ChildObject>(){}.getType()
) 

And you're done, no messiness, just simple json in, json out. If you don't have gson, I'm sure you have other json options available to you.

It's a WHOLE lot cleaner and faster than doing reflection and all that craziness.

Sam
  • 5,342
  • 1
  • 23
  • 39
0

You will need a copy constructor, but your copy constructor can use reflection to find the common fields between the two objects, get their values from the "prototype" object, and set them on the child object.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
0

You could use the reflection API to loop through each of the Car fields and assign the value to the equivalent Truck fields. This can be done within truck. Further it is the only way to access the private fields of Car - at least in an automatic sense, providing that a security manager is not in place and restricting access to private field.

Martin OConnor
  • 3,583
  • 4
  • 25
  • 32
  • 1
    Yes, you can do this sort of thing with reflection, or possibly with some preexisting framework. Careful about reflection, though. It's fine in some cases, but it still has some performance issues, so resist using it for high-volume stuff. – Don Branson Mar 12 '09 at 11:36