2
public class A {
    private int[] values;

    public int[] getValues() {
        return values;
    }
}

A book that I'm reading says that it is not immutable because values is a reference type. I see no way to mutate values other than creating a main method inside of the class.

j.doe
  • 1,214
  • 2
  • 12
  • 28

4 Answers4

4

It is mutable because you can change the contents of values through the method that you exposed

A obj = new A(...) // values gets filled here
int[] leak = obj.getValues()
leak[0] = 42

This would mean that your values property contains now information that has been modified externally. To make it immutable, you should return a copy of the array, so that you are sure no external agent can modify it, and you should make it final, so that no subclasses can be created that can expose the state and make it mutable again:

public final class A {
  private int[] values;

  public int[] getValues() {
    return Arrays.copyOf(values);
  }
}
Mario F
  • 45,569
  • 6
  • 37
  • 38
  • How do you assign values to the elements of a null array? – Andy Turner Aug 30 '17 at 20:59
  • I mean `values` is not final so that reference would start as `null`, but could get set to anything in another part of the class. – Mario F Aug 30 '17 at 21:04
  • But in that case we can easily change values with reflection, isn't it? I think we should divide immutable classes and immutable objects, because this one is immutable class, but it is not immutable object, because values is not final. Even if author assumes that somewhere else we change values, it means that we have method or constructor. If it is constructor it's probably fine (values is generated, but still ..reflection), if it is method, we can easily change it. – egorlitvinenko Aug 30 '17 at 21:34
  • @egorlitvinenko in what way is your "immutable" version immune from reflective modification? – Andy Turner Aug 30 '17 at 21:46
  • @AndyTurner When you have SecurityManager enabled in JVM you can't change final field with reflection. Do you know something else? – egorlitvinenko Aug 30 '17 at 22:21
  • @egorlitvinenko you can change the elements, can't you? – Andy Turner Aug 30 '17 at 22:25
  • @AndyTurner what my "immutable" version do you mean? I mean immutable class and object. If you provide copy of array, you can't change element. And in this answer, I mean there is only immutable class and Arrays.copyOf does not matter, because we can change values. – egorlitvinenko Aug 30 '17 at 22:25
1

It's not actually mutable through the values array, as stated in other answers: that array is null, as it is never initialized, and you can't make it non-null via the result of the getter.

It's mutable because you can subclass it, and add mutable state:

class AA extends A {
  String foo;
}

AA aa = new AA();
aa.foo = "foo";
A a = aa;
aa.foo = "bar";  // Mutates the state of a.

Note that there is a "Strategy for defining immutable objects" in the Oracle Java tutorial, as well as a list of requiried properties of immutable objects in Effective Java 2nd Ed Item 15: "Minimize mutability".

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • `int[] array = (new A()).getValues(); array = new int[5]();` the array itself is mutable too... – geneSummons Aug 30 '17 at 21:01
  • @geneSummons uh huh. And what's the value of `array`? – Andy Turner Aug 30 '17 at 21:02
  • well, technically that is correct. But in any real world program, `values` would be probably filled by some data that a caller can modify. So yeah, as is that class is not mutable (provided you make it `final`), but it is still not really designed to be immutable in practice – Mario F Aug 30 '17 at 21:02
  • Why do you create another class and assume that it still should be immutable? – egorlitvinenko Aug 30 '17 at 21:04
  • @egorlitvinenko I create another subclass to show that it is *mutable*. – Andy Turner Aug 30 '17 at 21:05
  • @geneSummons and what's the value of the array in the `A` after you write `array = new int[5];`? You just reassign the local variable, not the member variable. – Andy Turner Aug 30 '17 at 21:10
  • Oh, I understand. Immutable Objects/Immutable Classes, yeah, you are partially right. And you are partially wrong, because we can change foo, via reflection in A. – egorlitvinenko Aug 30 '17 at 21:12
  • @Andy Turner Because the declaration of the array member in class A is not `final`, any reference to an instance of that array can be reinitialized to a "new" reference value. The reinitialization may or may not impact the reference inside an instance of class A, it depends on the scope of the code block where the reinitialization occurs. – geneSummons Aug 30 '17 at 21:17
  • @geneSummons try it out. Post a link to an ideone demo demonstrating the behavior you are describing. ([Here's one I made earlier](https://ideone.com/nHIaDH)) – Andy Turner Aug 30 '17 at 21:19
  • @AndyTurner The interface `A` defines is still immutable though, `values` is private. Correct me if I'm wrong (not doing Java regularly), but any non-final Java class is (by your) definition mutable. – jasper Aug 30 '17 at 21:40
  • @jasper correct. Check out *Effective Java 2nd Ed* Item 15: this is item 2 in the list. – Andy Turner Aug 30 '17 at 21:43
  • @AndyTurner https://ideone.com/yn9C9T – geneSummons Aug 30 '17 at 21:53
  • @geneSummons you know that ideone has assertions switched off, right? https://ideone.com/lITL0w. – Andy Turner Aug 30 '17 at 21:54
  • 1
    @AndyTurner I did not know ideone.com has assertions switched off by default. I also did not know my local IDE also has assertions switched off by default for DEBUG launches of Java projects. I stand corrected sensei. – geneSummons Aug 30 '17 at 22:14
  • @AndyTurner Don't have that book available right now. Thinking about it, I would still consider `A` to be immutable. The state `A` represents (wrongly called it _interface_ in my previous comment) cannot be changed. Any consumer of `A` can (in terms of (im)mutability) not be broken by passing an instance of a derived type to it. Do you have an accessible source for the points supposedly made in _Effective Java_? – jasper Aug 30 '17 at 22:30
  • @jasper look up a PDF of EJ2 on your favorite search engine. It's on page 73. Don't forget that the interface of `A` also includes all of the methods in `Object`, so you can expose the mutable state e.g. via `toString()` or `hashCode()`. – Andy Turner Aug 30 '17 at 22:35
0

You can get reference and change element array:

 A.getValues()[0] = -100500

You can use reflection to change values field, or somewhere else (probably author means) or with inheritance as showed below.

For instance,

Immutable class:

import java.util.Arrays;

public final class A {
    private int[] values;

    public int[] getValues() {
        return values;
    }
}

Immutable object:

import java.util.Arrays;

public class A {
    private final int[] values;

    public A(int[] values) {
        this.values = null == values ? null : Arrays.copyOf(values);
    }

    public int[] getValues() {
        return Arrays.copyOf(values);
    }
}

Immutable class and object:

import java.util.Arrays;

public final class A {
    private final int[] values;

    public A(int[] values) {
        this.values = null == values ? null : Arrays.copyOf(values);
    }

    public int[] getValues() {
        return Arrays.copyOf(values);
    }
}
egorlitvinenko
  • 2,736
  • 2
  • 16
  • 36
0

values, being an array is a reference type. You can find out more about references here. This means that when getValues() returns values, it's actually returning a pointer to it (that gets dereferenced every time, since Java doesn't have explicit pointers). Although the reference itself cannot be reassigned since values is private, the contents can be changed.

So something like a.getValues()[0]++ will increment the first element of values, assuming it isn't empty.

Calvin Li
  • 2,614
  • 3
  • 17
  • 25