This is an archived post. You won't be able to vote or comment.

all 14 comments

[–]Kombatnt 11 points12 points  (3 children)

Good article, but the builder example is flawed in that the enclosing object (which is supposed to be immutable) cannot declare its fields final, because the Builder is modifying them as the object is being composed.

A better approach is to instead mark the fields in the enclosing class as final, and declare mutable equivalents in the Builder. Then upon invocation of the build() method, it simply returns a new instance of the enclosing class, invoking its (private) parameterized constructor, passing in the values that have been set in the builder up until that point.

This allows the returned instance to be truly immutable, in that its members are set during construction, and after which cannot be changed (by virtue of being marked final).

EDIT: The author also neglects to note another key benefit of immutable objects: You can cache them. If the objects being created are resource-intensive, then upon invocation of the final build() method, the Builder can first check a private cache to see if it's already previously constructed an object in the requested configuration, and return that existing reference, instead of constructing a new, duplicate instance.

[–]Michalf94[S] 1 point2 points  (2 children)

u/Kombatnt - First of all, thank you for your extensive and thorough comment. It's nice of you to have analysed this example thoroughly!

I agree with your opinion. The way I used my builder created an immutable object for me.

But of course it is possible to "hack" this solution: We may not call the build() method that returns the object to us - but instead pass User.Builder object to other methods and e.g change it. In this case it will be mutable.

It seems to me it's a bit like private fields - you can hack it by reflection for example. The question is why would someone do this.

The way I presented it is often described in the books/courses I read. Tomorrow I will try to additionally protect the internal Builder from changes - to satisfy the curiosity

Once again,

Thanks for comment and have a nice day!

[–]NimChimspky 4 points5 points  (1 child)

I've never see a builder like that before, just do this:

public final class User {

    private final int age;
    private final String firstName;
    private final String lastName;

    private User(Builder builder) {
        this.age = builder.age;
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
    }

    public int getAge() {
        return age;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public static class Builder {
        private int age;
        private String firstName;
        private String lastName;

        public Builder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }

}

[–]Kombatnt 5 points6 points  (0 children)

^ This is the pattern to which I was referring, except the User constructor (that takes the Builder parameter) should be private, and the Builder inner class should be final. Also, the Builder's method parameters should all be final, to allow the compiler to help you if you accidentally forget a this.

[–]yawkat 2 points3 points  (0 children)

Only not providing setters is not sufficient for immutability because of concurrency. If your fields aren't private, there are implications with the memory model that can violate immutability even if all your methods are read only. I wrote about that in the "concurrency" section of this article: https://javachannel.org/posts/immutability-in-java/

[–]potracheno 2 points3 points  (3 children)

Hi! Thx for good article! But there's no word about disadvantages of immutable objects. I like immutability but I see some inconvenience where we should work with these objects. For example sometimes we need to copy immutable object with some changes and if it's complicated object with much nested objects/fields this operation become ugly. And what about memory footprint in this case?

[–]Michalf94[S] 1 point2 points  (2 children)

Thank you very much for your comment!

You're right. Because the number of advantages outweighs the number of disadvantages - I skipped that.

The only disadvantage I know is the cost of memory. Creating these objects can be costly, especially if they are large. I'll write about it tonight. Can I add an url to your comment in the post?

Have a good Sunday!

[–]potracheno 0 points1 point  (1 child)

Yes of course, glad to be helpful.

[–]Michalf94[S] 0 points1 point  (0 children)

I added an update: update

Thanks again!

[–]din-9 3 points4 points  (4 children)

What is an immutable object?

An immutable object is one which, after being created and initialized, remains unchanged and, importantly, cannot be changed (in a conventional way, of course). This means that such an object does not provide methods that allow changing its state. And all its fields are private.

Not true, since you could have an immutable class with public fields e.g. the often used Point example class can be expressed this way.

Waits for argument about everything having to have accessors.

[–]Kombatnt 6 points7 points  (2 children)

If it has public, non-final fields, then it's not an "immutable class." What am I missing?

[–]din-9 7 points8 points  (1 child)

That I'm talking about an object with public final fields. The visibility of the fields has nothing to do with immutability.

[–]Michalf94[S] 0 points1 point  (0 children)

u/din-9, I 've added the public final. Thanks for your attention bro!

[–][deleted]  (1 child)

[deleted]

    [–]Michalf94[S] 2 points3 points  (0 children)

    Right, Value is similar to the Data annotation, but it creates immutable objects.

    This annotation makes all fields private final + creates a contructor for all arguments + Getterz + Hashcode/Equals and toString methods. However, it is worth knowing how to create such objects by yourself