Builder with deep clone

2,912 views
Skip to first unread message

Builder with copy

unread,
Oct 14, 2017, 11:09:53 AM10/14/17
to Project Lombok
Hello all, 

Sometimes when working with immutable object you need to create a copy and changing a couple of filed.
What would be nice is to have a deep cloner in the builder constructor something like : 

Person twin1 = Person.builder()
    .firstname("twin 1")
    .lastName("last name")
    .age(10)
    .build();

Person twin2 = Person.builder(twin1)
    .firstName("twin 2")
    .build();

Let me know what you think of it. 


alejandropg

unread,
Oct 21, 2017, 12:15:32 PM10/21/17
to Project Lombok
+1 I think this is a great idea because is a very common scenery.

Martin Grajcar

unread,
Oct 21, 2017, 2:03:53 PM10/21/17
to project...@googlegroups.com
This already exists as toBuilder(). But the second snippet can instead be written using @Wither as

Person twin2 = twin1.withFirstName("twin 2");

--
You received this message because you are subscribed to the Google Groups "Project Lombok" group.
To unsubscribe from this group and stop receiving emails from it, send an email to project-lombok+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

alejandropg

unread,
Oct 21, 2017, 2:44:24 PM10/21/17
to Project Lombok
True! I hadn't noticed

A complete example:

package com.alex.example.lombok
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;

@Data @Builder(toBuilder = true)
public class PostalCode {
   
@NonNull private final String province;
   
@NonNull private final String postalCode;
}

package com.alex.example.lombok;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class PostalCodeTest {
   
@Test
   
public void build_new_instance_in_the_same_package() {
       
final PostalCode postalCode = new PostalCode("province", "pc");

        assertEquals
("province", postalCode.getProvince());
        assertEquals
("pc", postalCode.getPostalCode());
   
}

   
@Test
   
public void build_with_builder() {
       
final PostalCode postalCode = PostalCode.builder().province("province").postalCode("pc").build();

        assertEquals
("province", postalCode.getProvince());
        assertEquals
("pc", postalCode.getPostalCode());
   
}

   
@Test
   
public void clone_with_builder() {
       
final PostalCode postalCode = PostalCode.builder().province("province").postalCode("pc").build();

       
final PostalCode cloned = postalCode.toBuilder().province("another province").build();

        assertEquals
("another province", cloned.getProvince());
        assertEquals
("pc", cloned.getPostalCode());
   
}
}


The syntax is a little strange and not too common. Usually is in the form:

PostalCode.builder(original).province("another value").build()

or even

PostalCode.builder().clone(original).province("another value").build()


Anyway I don't understand why is necessary the @Wither if the builder exists.


Thanks!
To unsubscribe from this group and stop receiving emails from it, send an email to project-lombo...@googlegroups.com.

Martin Grajcar

unread,
Oct 21, 2017, 4:21:59 PM10/21/17
to project...@googlegroups.com
Anyway I don't understand why is necessary the @Wither if the builder exists.

It's not. But it's handy when you need no builder or when you want to change a single property or two. Both

person.withFirstname("a").withLastname("b")

and

person.toBuilder().firstname("a").lastname("b").build()

create one throw-away object and I'd argue that the former is slightly more readable. For more than two changes, the builder creates less garbage.

alejandropg

unread,
Oct 21, 2017, 4:37:18 PM10/21/17
to Project Lombok
I understand 👍

And why the toBuilder() implementation? there is any performance reason? Maybe in this way you don't need to copy all the original object?

Martin Grajcar

unread,
Oct 21, 2017, 7:11:12 PM10/21/17
to project...@googlegroups.com
Having both is a bit redundant, but obviously a long "withX" chain is no good idea as you copy the object N times. For a single change, the builder is an overkill and produces garbage. So they both have a case when they perform better.

Moreover, @Wither predates @Builder and it used to be the only way how to keep sanity with many immutables. @Builder is much more complicated, and toBuilder() is IMHO a simple but powerful improvement.

Note that you need to ask for it explicitly using @Builder(toBuilder=true). You titled you question "deep clone", but all you get a shallow copy (which is just fine for immutables).

--
You received this message because you are subscribed to the Google Groups "Project Lombok" group.
To unsubscribe from this group and stop receiving emails from it, send an email to project-lombok+unsubscribe@googlegroups.com.

alejandropg

unread,
Oct 22, 2017, 5:04:30 AM10/22/17
to Project Lombok
Yep, I did not create the thread, and you're right that is not the best name.

For me this thread can be closed.


Thanks a lot!
To unsubscribe from this group and stop receiving emails from it, send an email to project-lombo...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages