3

I want to clone any LinkedList, whether or not it holds things which can be primitive wrappers. I understand it can be a deep recursive call to get a true deep clone - but I want just one level of cloning. I cannot compile the following code :

   <T> LinkedList<T> deepCloneOneLevel (final LinkedList<T> input){
        if(input != null){
            LinkedList<T> clone = new LinkedList<>();
            for (T t: input){   
                clone.add(t.clone());  //error : clone() has protected access
            }
            return clone;
        }
        return null;
    }
dev
  • 11,071
  • 22
  • 74
  • 122
  • 2
    You can't. Not all objects are cloneable. What are you trying to achieve, at a higher level? – JB Nizet Jan 23 '16 at 11:13
  • Small comment on your code: the `for` loop may be simplified to a `collect()` call using Java 8. – bcsb1001 Jan 23 '16 at 11:19
  • Even if I add a check (if t instanceof Cloneable); it doesn't work. Why is that ? Is there a workaround ? – dev Jan 23 '16 at 11:19
  • 1
    For a working example see [this answer](http://stackoverflow.com/a/715660/5717099). However, as JB Nizet said this requires all the objects inside that list to implement *cloneable*. Generally I would prefer an explicit copy constructor over using `.clone`, though. Rationale [here](http://www.artima.com/intv/bloch13.html). – morido Jan 23 '16 at 11:19
  • @bcsb1001: Thanks. But I am working on Android, which only support Java 7 as of now. – dev Jan 23 '16 at 11:19
  • 1
    @SlowAndSteady The reason for that is that `Cloneable` is a marker interface and doesn't, on the code level, guarantee a `public` `clone()` method. It _should_ have this if it follows the spec, but if you look at the actual source of `Cloneable` you will find a huge Javadoc followed by an empty `interface`. – bcsb1001 Jan 23 '16 at 11:21
  • 1
    The Cloneable interface is horribly designed: it doesn't guarantee that an object has a public clone() method. Instead, it just serves as a marker for the protected Object.clone() method to not throw an exception. You'd better define your own interface. – JB Nizet Jan 23 '16 at 11:21

2 Answers2

2

As mentioned in the comments, Java's Cloneable is not a very friendly method to clone an object. So, You might need to define your Cloneable interface, and make sure

interface MyClone<T>{
  T clone();
}

And use that in your code

 <T extends MyClone<? super T>> LinkedList<T> deepCloneOneLevel (final LinkedList<T> input){
    if(input != null){
        LinkedList<T> clone = new LinkedList<>();
        for (T t: input){   
            clone.add(t.clone());
        }
        return clone;
    }
    return null;
}

And implementations of MyClone know if a shallow copy is enough or a deep copy is required

But what if the type doesn't implement MyClone? Good question. We can add an overload that takes a clone "factory".

<T> LinkedList<T> deepCloneOneLevel (final LinkedList<T> input, Function<T,T> factory){
    if(input != null){
        LinkedList<T> clone = new LinkedList<>();
        for (T t: input){   
            clone.add(factory.apply(t));
        }
        return clone;
    }
    return null;
}

If your platform doesn't have Function yet, you can easily write your own, or use Guava's one.

Sleiman Jneidi
  • 22,907
  • 14
  • 56
  • 77
  • 1
    With this approach I might not be able to handle Integer and String types, right ? – dev Jan 23 '16 at 11:33
  • 1
    Accepting this answer, although I cannot use this in full, as Function in not available in Android/Java7 – dev Jan 23 '16 at 11:43
  • @SlowAndSteady you can easily define your own, no? – Sleiman Jneidi Jan 23 '16 at 11:44
  • I would use `UnaryOperator` rather than `Function` here. – bcsb1001 Jan 23 '16 at 11:48
  • I have never used Function and UnaryOperator before - guess it's time to read ! – dev Jan 23 '16 at 11:50
  • @SlowAndSteady you should really tell us what you want to achieve, at a higher level. Cloning Integer and String doesn't make any sense: they're both immutable, and cloning them only uses CPU cycles and memory for nothing. – JB Nizet Jan 23 '16 at 11:54
  • @JBNizet My wordings were incorrect. I meant to ask what if T is in a third party library and I cannot implement MyClone easily - and Sleiman Jneidi has already answered that part. – dev Jan 23 '16 at 11:59
1

Since clone() method is a big mess in java world, one solution may be to use apache commons SerializationUtils. It uses serialization to copy objects and as a result is slow. What is worse you'll have troubles if your class contains fields that do not support serialization. Here's an example:

<T extends Serializable> LinkedList<T> deepCloneOneLevel (final LinkedList<T> input) {
    if (input != null) {
        LinkedList<T> clone = new LinkedList<>();
        for (T t : input){
            clone.add(SerializationUtils.clone(t));
        }
        return clone;
    }
    return null;
}
AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76