Search code examples
javaimmutabilityeffective-java

Why is sharing the mutable internals of immutable objects a good idea?


I'm reading Joshua Bloch's "Effective Java" and have two questions. Bloch states the following.

Not only can you share immutable objects, but they can share their internals.

He then proceeds to give an example with one instance of BigInteger sharing its magnitude array with another BigInteger instance. My first question is: doesn't this violate the rule that Bloch laid out earlier, i.e.

Ensure exclusive access to mutable components.

As an example of how sharing mutable internals could be problematic, it seems to me that if two instances sample1 and sample2 of the same class can share their internals then you could have the following.


public final class Sample {

    private final int[] field1;
    private final int[] field2;

    public Sample(int[] field1, int[] field2) {
        this.field1 = field2;
        this.field2 = field2;
    }

    public Sample(int[] field1, Sample sampleForField2) {
        this.field1 = field1;
        for (int i = 0; i < sampleForField2.field2.length; i++) {
            sampleForField2.field2[i] += 1;
        }
        this.field2 = sampleForField2.field2;
    }

    public static void main(String[] args) {
        Sample sample1 = new Sample(new int[]{0, 1}, new int[]{10, 11});
        Sample sample2 = new Sample(new int[]{20, 21}, sample1);
        System.out.println(Arrays.toString(sample1.field2));
    }

}

In the example above, a is able to share its internals with b via the constructor public Sample(int[] field1, Sample sampleForField2). This leads to aliasing, causing sample1's field2 value to be {11, 12}.

Thus, my second question is: wouldn't sharing mutable internals between instances break immutability?


Solution

  • Its fine to share the internals of an immutable object, provided that you expose the internals of the object in an immutable way.

    For example:

    public class ImmutableObj {
    
      private final String[] internals;
    
      public ImmutableObj(String[] strings) {
        internals = Arrays.copyOf(strings, strings.length);
      }
    
      public String[] getInternals() {
        // return a copy of the internal state, since arrays
        // do not offer an immutable view
        return Arrays.copyOf(internals, internals.length);
      }
    }
    

    Arrays are an inefficient example, but it it still possible. A much more performant thing to do would be storing/exposing the internal state as some sort of Collection<String> so you can set the internals to an unmodifiable object to begin with:

    public class ImmutableObj {
    
      // Simply make this public instead of getter since it is read-only
      public final List<String> internals;
    
      public ImmutableObj(String[] strings) {
        internals = Collections.unmodifiableList(Arrays.asList(strings));
      }
    
    }