Tuesday, September 30, 2008

Finally fixing the Cloneable problem

So I finally had a use for Java's Cloneable interface. I needed to make a rather large object hierarchy cloneable. In java, to make an object cloneable (using the cloneable interface), you need to make it (or one of it's superclasses) implement cloneable, then you usually override Object's clone method with a public variant of it. That's not all however. If your object has instance variables which are non-primitive, and mutable (I.E. not String, Integer, Short, Long, etc... all the immutable types), your clone method has to handle cloning those objects itself.

Now this is all well and good, but there's one big problem with it... there is no guarantee that a subclass who needs to implement clone in order to correctly copy it's reference fields, actually does it. Even worse, if a class doesn't implement it, and you call cone on it, it will work, and just give you an incomplete copy of the object.

Since Java 5, they have added support for processing annotations at compile time, so I figured "why not a @IsCloneable annotation?".

Here's the jar. (Edit: 2010-10-12 The jar has been lost.)

So you have the code, now all you have to do is put that in your classpath and use apt instead of javac to compile your code. apt is a program included since Java 5 which accepts all the same options as javac, so it can be a simple drop-in replacement.

To use it in the code itself, you just need to annotate the head of the object hirearchy with @IsCloneable.

It also supports a few more advanced usages. If, for example, you have a class that you do not want to support cloning, just annotate the class with @NotCloneable and my code will stop enforcing policy right there.

Also, if you have a mutable reference field that (for whatever reason) you want to be shallow copied, just annotate that field with @SkipClone

P.S. At the moment, it is java 5 only. Java 6 support is coming.

P.P.S. It is GPL licensed code (source is in the jar), but I may relicense it if someone asks really nicely.

P.P.P.S. The current implementation has one limitation... you have to compile the entire object hierarchy at once. The only workaround for it is to annotate every class you compile separately with @IsCloneable