Java - Is this an immutable class?

Viewed 61

I have the following class:

 public final class MyClass {
    private final int[] arr;
    private final String str;
    private final int i;

    public MyClass(int[] arr) {
        this.arr = arr;
        this.str = "abc";
        this.i = arr.length;
    }

    public String getStr() {
        return str;
    }

    public int func(int j) {
        int res = this.i + j;
        return res;
    }

}

Is this class immutable? An object is considered immutable if its state cannot change after it is constructed. In this case, all of the fields are private final, so they obviously cannot change (i.e. being reassigned) after an object is constructed. However, I could code something like this:

int[] arr = {1, 2, 3};
MyClass myClass = new MyClass(arr);

// 1
myClass.arr[0] = 100;
System.out.println(Arrays.toString(myClass.arr)); // {100, 2, 3}

// 2
myClass = new MyClass(new int[] {100, 200, 300});
System.out.println(Arrays.toString(myClass.arr)); // {100, 200, 300}

Are 1 and/or 2 considered a change of state? This question appeared on my test, and my answer was that this class is indeed immutable. The correct answer was that the constructor needs to be changed in order for this class to be immutable. An answer which I don't quite understand. Any help would be appreciated

3 Answers

Because you can change the array from outside of the class, this class would not be considered immutable. However you can make it immutable by copying the array

public MyClass(int[] arr) {
    this.arr = Arrays.copyOf(arr,arr.length);
    this.str = "abc";
    this.i = arr.length;
}

This class is mutable.

Here is example that shows it

int [] arr = {1};

MyClass x = new MyClass(arr);

x.func(0); // Will return 1

arr[0] = 2;

x.func(0); // Will return 2

To fix that you need copy array in constructor.

    public MyClass(int[] arr) {
        this.arr = new int[arr.length];
        System.arrayCopy(arr, 0, this.arr, 0, arr.length);
        this.str = "abc";
        this.i = arr.length;
    }

This question appeared on my test, and my answer was that this class is indeed immutable. The correct answer was that the constructor needs to be changed in order for this class to be immutable.

The correct answer to the test is indeed correct. This class is not immutable.

In order to be immutable apart from encapsulating mutable fields properly by protecting them from external access and not exposing references to them, these references should not be shared with other parts of the code.

Seen reference to array proved via constructor is being assigned directly to the arr field, the same reference might end up somewhere where this array can be altered, and your object would change its state. Therefore, we can't consider it to be immutable.

Here's a quote from the tutorial provided by Oracle

Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

Related