Cloning constructor

5,377 views
Skip to first unread message

Matthew Jaggard

unread,
Apr 23, 2012, 7:57:08 AM4/23/12
to project...@googlegroups.com
I'm not sure how common a design pattern this is, but I've just had to implement a cloning constructor (see below) could Lombok create this for me?


@Data
public class Blah
{
   private int field1;
   private int field2;
   private int field3;

   public Blah(Blah toCopy)
   {
      this.field1 = toCopy.field1;
      this.field2 = toCopy.field2;
      this.field3 = toCopy.field3;
   }
}


Reinier Zwitserloot

unread,
Apr 23, 2012, 5:50:27 PM4/23/12
to project...@googlegroups.com
We could make an annotation that would make one for you, certainly. Lombok does not currently ship with such an annotation + handler, though.

Matthew Jaggard

unread,
Apr 23, 2012, 6:38:23 PM4/23/12
to project...@googlegroups.com
Thanks Reiner. 
I guess my reservation is that this is the first time I've wanted one in my current project, and I don't remember wanting loads before. It just seemed like a lots of typing for a fairly simple concept. Have other people come across this? It's not worth putting in if it's not at all common.

I guess @CloningConstructor would be a reasonable name? I would also assume that it would only make shallow copies of things, but that would have to be documented (so that if you have a Collection in your object, you don't expect the contents of the new one to remain static when you update the original).

--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/
 
To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

Kevin Ludwig

unread,
Apr 23, 2012, 6:54:48 PM4/23/12
to project...@googlegroups.com
maybe I'm missing something but can't you just implement cloneable? Doesn't that automatically provide a clone() method that does shallow copies?
--
Kevin

Tumi

unread,
Apr 24, 2012, 6:37:02 AM4/24/12
to Project Lombok
Yes, but the native Object.clone implementation can be 800% or 1000%
slower than this way...
Regards

Matthew Jaggard

unread,
Apr 24, 2012, 7:14:28 AM4/24/12
to project...@googlegroups.com
This page lists quite a lot of problems with clone()

Reinier Zwitserloot

unread,
Apr 24, 2012, 7:31:03 AM4/24/12
to project...@googlegroups.com
Yeah, the 'is this actually common boilerplate' issue stuck us too. Also, usually a copy constructor does a little more than 'just' copy fields, so how do we let you add some code? That's harder than it looks, at least if we want to do it in a way that makes it easy to understand and does not require you to dig through a lot of documentation to figure out how it works (i.e. no 'magic methods' which are called at the end).



 --Reinier Zwitserloot

Matthew Jaggard

unread,
Apr 24, 2012, 8:06:32 AM4/24/12
to project...@googlegroups.com
In my particular case, I don't actually want to do any more than copy the fields. If you did, could you have a parameter to the annotation like this?

@Data
@CopyConstructor(after="postCopy")
public class MyObject
{
   private int field1;

   private void postCopy()
   {
      field1 = 23;
   }
}

Obviously this creates nasty issues like what to do if the method doesn't exist? (Error at compile time)
The other option I see (again, I'm not sure if this is possible?) is to have a separate annotation like @AfterCopyConstructor

@Data
@CopyConstructor
public class MyObject
{
   private int field1;

   @AfterCopyConstructor
   private void postCopy()
   {
      field1 = 23;
   }
}

In this case, what happens if @AfterCopyConstructor is defined on a method but @CopyConstructor is not defined? (Fail to create the constructor silently?)

Roel Spilker

unread,
Apr 25, 2012, 5:26:28 AM4/25/12
to project...@googlegroups.com
I like to use final fields a lot, so I having a postCopy method is no solution is I would like to clone/modify the contents of the fields in the constructor.

On 24-4-2012 14:06, Matthew Jaggard wrote:
In my particular case, I don't actually want to do any more than copy the fields. If you did, could you have a parameter to the annotation like this?

@Data
@CopyConstructor(after="postCopy")
public class MyObject
{
� �private int field1;

� �private void postCopy()
� �{
� � � field1 = 23;
� �}
}

Obviously this creates nasty issues like what to do if the method doesn't exist? (Error at compile time)
The other option I see (again, I'm not sure if this is possible?) is to have a separate annotation like @AfterCopyConstructor

@Data
@CopyConstructor
public class MyObject
{
� �private int field1;

� �@AfterCopyConstructor
� �private void postCopy()
� �{
� � � field1 = 23;
� �}
}

In this case, what happens if @AfterCopyConstructor is defined on a method but�@CopyConstructor is not defined? (Fail to create the constructor silently?)

On 24 April 2012 12:31, Reinier Zwitserloot <rei...@zwitserloot.com> wrote:
Yeah, the 'is this actually common boilerplate' issue stuck us too. Also, usually a copy constructor does a little more than 'just' copy fields, so how do we let you add some code? That's harder than it looks, at least if we want to do it in a way that makes it easy to understand and does not require you to dig through a lot of documentation to figure out how it works (i.e. no 'magic methods' which are called at the end).



�--Reinier Zwitserloot




On Tue, Apr 24, 2012 at 12:14, Matthew Jaggard <mat...@jaggard.org.uk> wrote:
This page lists quite a lot of problems with clone()



On 24 April 2012 11:37, Tumi <elbailed...@gmail.com> wrote:
Yes, but the native Object.clone implementation can be 800% or 1000%
slower than this way...
Regards

On Apr 24, 12:54�am, Kevin Ludwig <kevin.lud...@gmail.com> wrote:
> maybe I'm missing something but can't you just implement cloneable? Doesn't
> that automatically provide a clone() method that does shallow copies?
>

--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/

To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/
�

To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/
�

To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en
--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/
�

Matthew Jaggard

unread,
Apr 25, 2012, 5:44:56 AM4/25/12
to project...@googlegroups.com
Hmm. I'm guessing the IDE / javac wouldn't be clever enough to work out that the following is technically OK...

public class MyClass
{
  private final int i;

  public MyClass(MyClass copyOf)
  {
    afterCopy();
  }

  private void afterCopy()
  {
    i = 23;
  }

  //afterCopy is never called in the rest of the class.
}

Although actually, this is kind of irrelevant because the constructor has to NOT initialise the final variable "i" - and we have no way of knowing that's the case, unless you had YET ANOTHER annotation called @CopyNonFinalConstructor or something?!

It's getting too complicated. Maybe it's just not worth adding at all?



On 25 April 2012 10:26, Roel Spilker <r.sp...@topdesk.com> wrote:
I like to use final fields a lot, so I having a postCopy method is no solution is I would like to clone/modify the contents of the fields in the constructor.

On 24-4-2012 14:06, Matthew Jaggard wrote:
In my particular case, I don't actually want to do any more than copy the fields. If you did, could you have a parameter to the annotation like this?

@Data
@CopyConstructor(after="postCopy")
public class MyObject
{
   private int field1;

   private void postCopy()
   {
      field1 = 23;
   }
}

Obviously this creates nasty issues like what to do if the method doesn't exist? (Error at compile time)
The other option I see (again, I'm not sure if this is possible?) is to have a separate annotation like @AfterCopyConstructor

@Data
@CopyConstructor
public class MyObject
{
   private int field1;

   @AfterCopyConstructor
   private void postCopy()
   {
      field1 = 23;
   }
}

In this case, what happens if @AfterCopyConstructor is defined on a method but @CopyConstructor is not defined? (Fail to create the constructor silently?)

On 24 April 2012 12:31, Reinier Zwitserloot <rei...@zwitserloot.com> wrote:
Yeah, the 'is this actually common boilerplate' issue stuck us too. Also, usually a copy constructor does a little more than 'just' copy fields, so how do we let you add some code? That's harder than it looks, at least if we want to do it in a way that makes it easy to understand and does not require you to dig through a lot of documentation to figure out how it works (i.e. no 'magic methods' which are called at the end).



 --Reinier Zwitserloot




On Tue, Apr 24, 2012 at 12:14, Matthew Jaggard <mat...@jaggard.org.uk> wrote:
This page lists quite a lot of problems with clone()

Maaartin G

unread,
Apr 25, 2012, 7:32:28 AM4/25/12
to project...@googlegroups.com
On Wednesday, April 25, 2012 11:26:28 AM UTC+2, Roel Spilker wrote:
> I like to use final fields a lot, so I having a postCopy method is no solution is I would like to clone/modify the contents of the fields in the constructor.

Exactly. This is my major problem both with the proposed CloningConstructor and the clone() method. My second largest problem is the need to "unshare" some members via something like list = new ArrayList(original.list). I intentionally don't write "deep copy", since it goes just one level deep (in fact I don't think I ever needed a real deep copy).

What I needed is something like

@lombok.RequiredArgsConstructor
class Something {

    private final Date date;
    private final int x, y, z;
    private final List list;

    @lombok.Complete
    public Something(Something original) {
        // here Lombok inserts missing assignments for x, y, and z
        list = new ArrayList(original.list);
        date = new Date(original.date.getTime());
    }
}

This could be brought a bit further like

    @lombok.Complete
    public Something(Something original, int x) {
        // here Lombok inserts missing assignments for x, y, and z
        this.x = x;
        list = new ArrayList(original.list);
        date = new Date(original.date.getTime());
    }

Karsten Tinnefeld

unread,
Apr 25, 2012, 5:52:31 PM4/25/12
to project...@googlegroups.com
In a anemic data object generator before our time, we had copy() methods which generated copy() methods on the source objects to produce a new object. That way, you could access private target fields with
  1. assignments on simple values: newObject.a = a;
  2. produce copies on complex values: newObject.b = b.copy();
  3. optionally create deep copies on arrays: newObject.c = new XXX[c.length]; System.arraycopy(c, 0, d, 0, c.length);
  4. optionally create deep copies on collections: newObject.d.addAll(d); // where newObject.d was initialized as some Collection in the constructor.
  5. newObject = (XXX) this.clone() if everything else failed.
We missed out the special java.util.Date case. Date should be considered immutable by convention.
I guess that lombok could not distinguish between cases 1 or 2 and the option in 3/4 would need a switch as well.

Reinier Zwitserloot

unread,
Oct 6, 2012, 1:44:13 PM10/6/12
to project...@googlegroups.com
I think its cool but I worry that it becomes a feature that sees very little use and becomes a pain to maintain. There is so much that can be configured about this sort of thing, so do we make it as simple as possible and simply resort to: "Well, if this simplest case does not work for you, then don't use lombok for it and write it out longhand", or make it configurable (which makes the feature much more complicated).

On Thursday, October 4, 2012 10:04:47 PM UTC+2, Peter Grant wrote:
Is anyone still interested in lombok annotations for this?  I've written a javac handler that does some of the things described here and would be happy to contribute that and more.  However most of my use cases center around interfaces, so I'm primarily interested in generating conversion constructors that assign fields based on calling getters in an implemented interface or superclass.

I still have plenty of work to do, but I have implemented the following so far:

1. Copying a basic data contract.  If two classes implement the same interface and have fields defined such that an @Data annotation generates implementations of the interface, annotating the classes with @CopyConstructor(TheSharedInterface.class) will generate a conversion constructor with an argument of TheSharedInterface.  Some customization would be useful here.
2. Deep copying of any class whose members all have a deep copy constructor.  Whether to try deep or shallow copying is a configuration option.  The trouble with deep copying is of course all of the iterables.
3. Deciding when to call super and throwing errors if we can't handle anything super likely didn't handle for us.
4. Determining type parameters for generics (including for deep copying and when inheritance and generics are involved).

The parts I have not yet implemented (but plan to) include:

5. Handle iterables. If the result of a getter is an iterable, the field type might hint at which iterable to use (i.e. List<T> field1 or LinkedList<T> field2).  If no implementation class is specified, I will probably end up picking a default per collection type.
6. Enable constructor extensibility.  I have currently defined an @CopyConstructor annotation at the class level, with an optional class argument telling it what type to copy from.  I like the idea of having a class-level annotation, since requiring a constructor would mean requiring unnecessary boilerplate.  Perhaps I could change the annotation to be definable at the class or constructor level: if it's at the class level, generate the constructor; if it's on a constructor, inject the generated code into the start of the constructor.


I've toyed with generating multiple constructors, compositing constructors, and static factory methods, but I didn't like where I got with any of that. (Example: constructors on a class are obvious, but how do you know a static method is a factory method if it's already been compiled and your annotation has a source retention policy?  That makes it nigh impossible to do deep copying.)

If there is any interest in including something like this in lombok, I would be happy to share it and keep plugging away on it.  Of course the biggest thing missing, by far, is the entire Eclipse implementation, but there are some other features that might be desirable to a broader audience, such as support for fluent getters or direct field access.  

Anyway, it's a start.  Thoughts?

Eero Aaltonen

unread,
Nov 20, 2014, 9:59:51 AM11/20/14
to project...@googlegroups.com
On Saturday, October 6, 2012 8:44:13 PM UTC+3, Reinier Zwitserloot wrote:
I think its cool but I worry that it becomes a feature that sees very little use and becomes a pain to maintain. There is so much that can be configured about this sort of thing, so do we make it as simple as possible and simply resort to: "Well, if this simplest case does not work for you, then don't use lombok for it and write it out longhand", or make it configurable (which makes the feature much more complicated).

I just encountered a situation where I need clone + copy constructors for an entire class hierarchy. The simplest implementation of a @CopyConstructor would already have been vastly useful.

Jozsef P

unread,
Jan 4, 2016, 7:33:23 PM1/4/16
to Project Lombok
I agree. I'm also in a situation where I need a basic copy constructor, as simple as in here:
public MyClass{
private int a;
private int b;
private String c;
private SomeClass d;

public MyClass(MyClass original){
this.a=original.a;
this.b=original.b;
this.c=original.c;
this.d=original.d;
}
}

When you have lots of fields, it becomes bothersome to implement such a boilerplate shallow copy constructor.
It would be much more elegant to simply annotate my class with @CopyConstructor, which would fit pretty well into the already existing @...Constructor annotations.

I see that configuring such an annotation can get pretty complex, but in my opinion, Lombok having a basic @CopyConstructor is still better than having no such functionality.

Martin Grajcar

unread,
Jan 5, 2016, 4:19:42 AM1/5/16
to project...@googlegroups.com
There's a workaround: Use @Wither on a dummy field like withDummy(!dummy) or on a field you change forth and back like withN(n+1).withN(n). Since v1.16.6, there's another workaround: Use @Builder(toBuilder=true) and build the clone. That said, it's all ugly and there are some unneeded allocations.

I usually use

    @Override public T clone() {
        try {
            @SuppressWarnings("unchecked")
            final T result = (T) super.clone();
            return result;
        } catch (final CloneNotSupportedException e) {
            throw new MyImpossibleException(e);
        }
    }

which is ugly, too.

I see that configuring such an annotation can get pretty complex, but in my opinion, Lombok having a basic @CopyConstructor is still better than having no such functionality.

Agreed. We could discuss for ages, what options a @CopyConstructor should have, but without any options, the semantics is perfectly clear.

There's nothing special about the @CopyConstructor. Whatever feature it should get would be also usable for the other constructors (and probably builder, or getter, or others). Features like special handling of mutable fields would be nice to have, but not only here.

Jozsef P

unread,
Jan 7, 2016, 6:30:46 PM1/7/16
to Project Lombok
 Thank you for the tips.  (y)
I think I will take the @Builder(toBuilder=true) approach, since I'm already using the @Builder annotation.
Reply all
Reply to author
Forward
0 new messages