Tuesday, August 3, 2021

Deep cloning a generic List in Java

Today I had to deep clone objects in Java. What a nightmare.

First, the Cloneable interface is horribly broken, and writing such a simply code becomes a laborious task. Second, if the object has a List as a field, you must manually clone the elements into a new List.

The code goes as it follows:

public class Foo implements Cloneable {

	private String name;

	private List words;

	@Override
	public Object clone() {
		try {
			Foo cloned = (Foo) super.clone();
			cloned.words = Auxiliar.cloneList(words);
			return cloned;
		} catch (CloneNotSupportedException e) {
			System.out.println("Never happens!");
			return null;
		}
	}

	// Getters and setters omitted.

}

Notice the Another.cloneList() call? This is where we manually clone all the List elements, by calling clone() in each one. This is the solution I found for a generic method:

public class Auxiliar {

	@SuppressWarnings("unchecked")
	public static <T extends Cloneable> List<T> cloneList(List<T> list) {
		try {
			List<T> newList = new ArrayList<>(list.size());
			for (T item : list) {
				newList.add((T) item.getClass().getMethod("clone").invoke(item));
			}
			return newList;
		} catch (IllegalAccessException | IllegalArgumentException
			| InvocationTargetException | NoSuchMethodException | SecurityException e) {
			System.out.println("Never happens!");
			return null;
		}
	}
}

No idea why Cloneable was not deprecated and replaced by something minimally decent yet.

No comments: