Deep Copying, Shallow Copying and Reflection

Deep Copying, Shallow Copying and Reflection Part 1

Its been quite busy over the last month. I have a long blogOpen-mouthed smileI have been looking at deep and shallow copying in Java. The Object.clone() method performs shallow copying.

That is if object A has a object B(by composition). Then our class would look something like the following.

public class A{

B b;

someMethod(B b){

this.b = b;

}

}

class B{

//methods and variables of its own

}

When we look at the above class and clone an instance of A we would perform the following:

A a= //some value that is an instance of A;

A newA = a.clone();

However a.clone() makes an identical copy of the object that a refers to but not copies of the objects that are composed in it like b! So programmatically this looks like:

(newA.b).equals(a.b) = true!!

newA and a share the same b hence they have references to the same instance of B.

How can this be cured? Manually we can clone b and say:

newA.b = (a.b).clone();

This works however we want to be able to do this in an automatic way. Imagine performing this task manually for larger classes with references to many objects!

We can use reflection to create one such tool. We need a tool that must by the use of reflection find out which objects are composed in a class and clone them and continue this for those objects until we reach a class that has no more objects composed in it or it is composed of primitives. This sounds like recursion.

This seems well but there are problems:

  1. If an object has overloaded constructors which one will we know was used to create the instance of the object that we want to copy?

  2. What about private members that prohibit access from the outside?

The solution would be to use any public constructor and use default values for the arguments then create a new instance. Then we can set the values of the members from the object we want to copy. To solve this we know that Java Reflection API allows us access to even private members of a class so problem 2) is solved.

What we then need to do is to create a program that can recursively examine a class analyse the members clone them and continue doing so for the whole class.

Now a great problem comes to mind: What if the members of the class have objects that themselves have other objects by composition? So we cannot use the Object.clone() method!

The solution to this lies in being able to perform recursion up to a certain point where we can then stop and where the members of the objects have a structure that is well know and can be manipulated for our purposes.

So we come to one set of classes that form a very important basis of the Java language- The wrapper classes and their primitives.

Use the facts that we know about the primitive and the wrapper classes we could make the following assumption

Primitives and Wrappers form the basis of many classes.

Of course we need the String class as well as many other classes that form a concrete base.

So we can generalise this idea into a concept of basis classes. These have

well known structures like the wrappers and String class we know how to create replicas.

Turning all the above ideas into code We will have the following. I am assuming familiarity with the Reflection API.

Clone Utility

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Modifier;

import java.util.LinkedList;

import java.util.List;

public class CloneUtility<T> {

public enum PrimitiveType {

INTEGER, FLOAT, CHAR, DOUBLE, SHORT, BYTE, VOID, BOOLEAN, LONG

}

public T cloneObject(T objectToBeCloned) {

T newT;

Class clazz = objectToBeCloned.getClass();

Constructor[] constructors = getConstructorOfClass(clazz);

if (constructors.length > 0) {

newT = (T) createNewObject(clazz, constructors[0]);

} else {

newT = (T) createNewObject(clazz, null);

}

Field[] fields = getInstanceVariables(newT.getClass());

copyVariables(fields, objectToBeCloned, newT);

return newT;

}

public Object createNewObject(Class clazz, Constructor constructor) {

Object newObject = null;

try {

if (constructor != null) {

Object[] parameters = constructor.getParameterTypes();

Object[] parametersNull = null;

if (parameters.length > 0) {

parametersNull = new Object[parameters.length];

if (parametersNull.length > 0)

newObject = constructor.newInstance(parametersNull);

}

if (newObject == null)

newObject = clazz.newInstance();

} else {

newObject = clazz.newInstance();

}

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (InstantiationException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (InvocationTargetException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return newObject;

}

public Constructor[] getConstructorOfClass(Class clazz) {

Constructor[] constructors = clazz.getConstructors();

return constructors;

}

public Field[] getInstanceVariables(Class cls) {

List accum = new LinkedList();

while (cls != null) {

Field[] fields = cls.getDeclaredFields();

for (int i = 0; i < fields.length; i++) {

if (!Modifier.isStatic(fields[i].getModifiers())) {

accum.add(fields[i]);

}

}

cls = cls.getSuperclass();

}

Field[] retvalue = new Field[accum.size()];

return (Field[]) accum.toArray(retvalue);

}

public void copyVariables(Field[] fields, T objectFrom, T objectTo) {

Object fieldToo = null;

Object obj = null;

Object objToo = null;

int type = 1;

try {

for (Field field : fields) {

Class clazzTo = field.getType();

if (clazzTo.isPrimitive()) {

obj = field.get(objectFrom);

Object[] result = checkPrimitiveType(field, obj, 1);

while (result == null && type <= 8) {

result = checkPrimitiveType(field, obj, type++);

}

if (result.length != 0) {

fieldToo = result[1];

field.set(objectTo, fieldToo);

}

} else {

obj = field.get(objectFrom);

CloneUtility cloneUtility = new CloneUtility();

fieldToo = cloneUtility.cloneObject(obj);

field.set(objectTo, fieldToo);

}

}

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

public Object[] checkPrimitiveType(Field field, Object objectFrom, int type) {

Object[] result = new Object[2];

Constructor constructorTo = null;

try {

Object obj = objectFrom;

Class[] parameterTypes = new Class[1];

// Class types for primitives

Class intClass = Class.forName("java.lang.Integer");

Class shortClass = Class.forName("java.lang.Short");

Class charClass = Class.forName("java.lang.Character");

Class byteClass = Class.forName("java.lang.Byte");

Class longClass = Class.forName("java.lang.Long");

Class booleanClass = Class.forName("java.lang.Boolean");

Class floatClass = Class.forName("java.lang.Float");

Class doubleClass = Class.forName("java.lang.Double");

switch (type) {

case 1:

parameterTypes[0] = Integer.TYPE;

constructorTo = intClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.INTEGER;

break;

case 2:

parameterTypes[0] = Short.TYPE;

constructorTo = shortClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.SHORT;

break;

case 3:

parameterTypes[0] = Character.TYPE;

constructorTo = charClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.CHAR;

break;

case 4:

parameterTypes[0] = Float.TYPE;

constructorTo = floatClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.FLOAT;

break;

case 5:

parameterTypes[0] = Byte.TYPE;

constructorTo = byteClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.BYTE;

break;

case 6:

parameterTypes[0] = Boolean.TYPE;

constructorTo = booleanClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.BOOLEAN;

break;

case 7:

parameterTypes[0] = Double.TYPE;

constructorTo = doubleClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.DOUBLE;

break;

case 8:

parameterTypes[0] = Long.TYPE;

constructorTo = longClass.getConstructor(parameterTypes);

result[0] = PrimitiveType.LONG;

break;

}

if (constructorTo != null && obj != null) {

Object fieldToo = constructorTo.newInstance(obj);

result[1] = fieldToo;

return result;

}

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (InstantiationException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (NoSuchMethodException e) {

// checkPrimitiveType(field,objectFrom,type+1);

result = null;

} catch (IllegalArgumentException e) {

// checkPrimitiveType(field,objectFrom,type+1);

result = null;

} catch (InvocationTargetException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return result;

}

}

Next we move on to Part2

Advertisements

About gjcbell

I'm a software designer and integration specialist in Cape Town, South Africa. I work for SPF at SGH. I develop applications in Java, Cplusplus and Python and JavaScript. I design websites and web applications.
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s