Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When working with immutable objects, how best do you return a copy with modified field(s)?

The Java 8 Streams API lends itself towards writing code functionally, rather than imperatively. As we know immutability offers many benefits and as such, I try to make objects immutable wherever practical. In day-to-day programming, I find myself in a situation where I'd like to "set" a value. My objects are immutable though, so I need to create a new object and initialise the field in the constructor.

I use project Lombok, which provides annotations such as @Value which essentially makes the object immutable. It also has @Builder which uses the builder pattern to provide a builder for the immutable object, setting fields unmentioned in the fluent API to null.

The @Builder annotation has a field named toBuilder, which, when set to true, provides the toBuilder() method, which returns a builder populated with the fields from the object, where the developer can "set" values, call build() and return a new object.

E.G. to create a List of immutable objects with modified forename fields, I'd do the following:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import lombok.Builder;
import lombok.Value;

public class SOExample {

    @Value
    @Builder(toBuilder = true)
    private static class Person {
        private final String forename;
        private final String surname;
        private final int age;
        private final int heightInCm;
    }

    public static void main(String[] args) {
        List<Person> people = Arrays.asList(Person.builder()
                .forename("stack")
                .surname("overflow")
                .age(21)
                .heightInCm(180)
                .build());
        people.stream()
                .map(p -> p.toBuilder()
                        .forename("updatedForename")
                        .build())
                .collect(Collectors.toList());
    }
}

Without using Lombok, this would require a lot of boiler-plate code. In fact, I've looked at the generated code and it's neither small nor trivial. This leads me to question myself. How are others out there doing it? I worry I'm missing a trick.

Taking into consideration the description and example above, what is the best way of returning a copy of an immutable object with updated field(s) and how would it be used in the Streams API?

like image 847
Robert Bain Avatar asked Sep 08 '17 23:09

Robert Bain


People also ask

Can an immutable object be modified?

An immutable object cannot be modified after it was created. Immutable objects have two great advantages: Code based on immutable objects is clearer and likelier to be correct. Bugs involving unexpected changes simply can't occur.

How can we maintain immutability of a class with a mutable reference?

Create a copy of the mutable object (i.e. via copy constructor, cloning, serialization/deserialization, etc.); never store the reference to the original mutable object. Never return the mutable object. If you must to, then return a copy of the object.

Which are true statements an immutable object can be modified?

An immutable object cannot be modified. An immutable object can be garbage collected. An immutable object cannot be garbage collected. String is immutable.


1 Answers

How often do you find you're having to do this?

If you're doing this in place of having modifiable classes, then perhaps it's a sign that really immutability doesn't make sense for your type.

If you're not doing it all that regularly, then just generating a new one with the accessors (and modifications to values) from the original doesn't seem too bad; nor does the Builder example you showed.

Person personA = ...

Person personB = new Person("updated", personA.getSurname(), personA.getAge(), ...);
like image 148
BeUndead Avatar answered Nov 14 '22 21:11

BeUndead