4

The Question

In Java, according to standard code style and design principles, when is it more appropriate to modify an object's internal data, and when is it more appropriate to create a new object using the modified data?

For example, let's say I have some class, Vector2D, which holds an x component and a y component, and I want to rotate that vector:

Approach A

public class Vector2D{
    private int x;
    private int y;
    public Vector2D(int x, int y){
        this.x = x;
        this.y = y;
    }
    public void rotate(double angle){
        //code for finding rotated vector
        x = rotated_x;
        y = rotated_y;
    }
}

A Vector2D object could then be rotated with vector1.rotate(angle)

Approach B

public class Vector2D{
    private final int x;
    private final int y;
    public Vector2D(int x, int y){
        this.x = x;
        this.y = y;
    }
    public Vector2D rotate(double angle){
        //code for finding rotated vector
        Vector2D rotated = new Vector2D(rotated_x, rotated_y);
        return(rotated);
    }
}

A Vector2D object could then be rotated with vector1 = vector1.rotate(angle)

Findings so far

My research into the issue so far has led me to believe that approach B is useful for allowing method chaining, e.g. vector1 = vector1.rotate(angle).scale(scalar), which has it's own benefits, more on the readability side of things.

It has also led me to realise that approach B allows you to make an object immutable, so I suppose by extension my question is also:

When is it appropriate to make an object immutable, especially in this example case? (and in general, for Points, Vectors, Polygons, etc. Should all these types be immutable?)

Edit:

The project I had in mind while creating this post is a game, in which entities are represented by Polygons, and are manipulated using vectors.

I believe that because of intended application, making sure the classes are thread-safe should not be an issue, as Vector and Polygon data should only ever be altered by the logic thread anyway.

This also means that these operations could potentially be being done 60 times per second (every game tick), for every single entity. Does this mean that any overheads from instantiating new objects each time could quickly add up?

6 Answers 6

5

In general, you should prefer making objects immutable. Reasons you might want to opt for mutable objects are:

  1. The objects have a lot of state and are thus expensive to create. E.g. imagine a massive matrix. If you don't allow mutation you'll have to duplicate the matrix every time, which could be prohibitively expensive (see pandas for example - it offers both options).
  2. Making it immutable makes the API very unintuitive. This also depends a bit on the history of the language. For example, most sorting libraries in e.g. Java mutate the collection rather than returning a copy. If you are going to implement shuffle,it might be better to mutate the list even if you are not worried about (1), just because it would be surprising if you return a copy.

Some languages and libraries came up with a third way, in which mutation returns a new "version" of an object while sharing the previous state (see this). This solves (1), thereby allowing you to benefit from immutability without the cost of copying a lot. Some argue this means you should practically always opt for immutability because with (1) gone, there is no longer any reason to opt for mutability other than familiarity. In reality, some algorithms are very hard to express without mutable states (e.g. see this). so we'll probably continue to have mutable states.

Sign up to request clarification or add additional context in comments.

Comments

2

It depends on what type of application you will be implementing.

If it is the case of multi-threaded one where you have a lot of threads doing tasks in parallel, the second option will come handy, because immutable objects are inherently thread-safe, and you will avoid a lot of potential headaches. But this approach also comes with a compromise, you will have the overhead of creating new object for every modification to the internal state of the object.

On the other-hand, if you are sure that you can avoid parallel access hazards, you can use the first approach which is of course faster in direct comparison since it avoid a lot of objects instantiations.

2 Comments

What are the actual overheads in involved with creating new objects? In my example, vector1 = vector1.rotate(angle) the "old version" of the vector no longer has a reference, and so will be cleaned up by the garbage collector, right? Does this cleaning by the garbage collector use up processing power? Additionally, how does the actual instantiation of the new object compare with just changing the internal values? Does it use more processing power and/or more main memory?
@JakeLawrence For once, garbage collection doesn't run at all times. If you have objects with no reference they will be eventually garbage collected but you don't know exactly when, and so at a particular point of time you will have potentially a lot of these objects in memory that are not used. Still there is the bigger risk to create a memory leak by keeping a reference mistakenely. Even the work of the garbage collector does require some OS resources of course, the more job does more resources are consumed.
2

my research into the issue so far has led me to believe that approach B is useful for allowing method chaining, e.g.

With the A approach you could both have setters and return the modified object for these methods. So you would have also the "chaining" feature.

So the real question is rather :

When is it appropriate to make an object immutable ?

As you want to guarantee multiple things for an object such as :

  • no side effect on it as any possible modification method will create and return a new instance
  • also no need to make defense copy for any instance of the class as it is de facto guarantee by the class itself.
  • no worry about race conditions : the instance cannot be modified
  • invariant rules setting

The Java 8 date classes (LocalDate and LocalTime) are good examples of relevant usage of immutability.

In your actual example, suppose Vector2D is an API class that can be passed to different methods of different kinds of objects.
With a mutable class, you can create any side effect on the instance as you pass the object to some methods. So it may change in a no desired way the object state for some clients of the class.
To avoid it clients of the class could make defensive copy. But it requires boiler plate and repetitive code for clients. With a immutable class, this problem cannot occur. Clients just may use the instance as they need without worrying about the other clients usage.

Comments

0

For me it depends. Mutable objects are dangerious for multithread app, also when u are going to pass your object through methods and dont want get them changed you should make it immutable.

So in genera umif you dont have any special problems as above you can go with mutable objects to avoid extra work for yourself, jvm and gc :)

Comments

0

In Approach A you could still return the object being modified and use the fluent API. It is not exclusive to second approach.

Regarding immutability, there are two main reasons:

  1. Immutable objects are simpler as once they are created there is no way to change their state, so it is not necessary to make validations on mutator methods.

  2. Immutable objects are inherently thread-safe. They cannot be corrupted by multiple threads accessing them concurrently.

I recommend that you read Effective Java - Third Edition, Item 17 to know more about the subject. I believe it is an amazing book for everyone interested in being a better developer.

Comments

0

It is good practice to use immutable objects whenever possible, which are easier to maintain and using which you can write code in a more functional way.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.