0

I still have some problems grasping the idea of immutability in Java. I understand that it differs from the const-ness in C++ and that a final class that only has final members of classes that are immutable themselves is immutable. E.g. the following class is immutable:

public final class A {
    final String x;
    final int y;

    public A(String x, String y) {
        this.x = x;
        this.y = y;
    }
}

Is there some formal definition besides the guidelines presented here and similar stuff somewhere else?

Consider the following example. Is Person immutable? Is there a way to make it immutable besides making the members mother and father final. I cannot make them final because I have to build a list of People objects from an input file with arbitrary sorting and do not want to perform topological sort on this input. Also, the case of cycles should be possible to be represented.

public final class Person {
    Person father = null;
    Person mother = null;
    public final String name;

    Person(String name) { this.name = name; }

    public Person getFather() { return father; }
    public Person getMother() { return mother; }
}

// in the same package

public class TrioBuilder {
    // build trio of child, mother, and father
    public static ArrayList<Person> build(String c, String m, String f) {
        Person child = new Person(c);
        Person mother = new Person(m);
        Person father = new Person(f);

        child.father = father;
        child.mother = mother;

        ArrayList<Person> result = new ArrayList<Person>();
        result.add(child);
        result.add(mother);
        result.add(father);
        return result;
    }
}
Manuel
  • 6,461
  • 7
  • 40
  • 54
  • 1
    A final class has nothing to do with mutability. It only means that you cannot extend from it – Patrick Jan 09 '15 at 12:44
  • @for3st that is not the same as an _immutable_ class – fge Jan 09 '15 at 12:45
  • `final` keyword has different meaning depending on what element is applied. – Héctor Jan 09 '15 at 12:46
  • 1
    @bigdestroyer this. Eg. BigDecimal is immutable though not a final class – Patrick Jan 09 '15 at 12:46
  • I am aware of the different meanings of `final`, but it was my understanding that a non-`final` class `A` is not immutable since you might inherit `B` from it and add setters, then pass a mutable `B` as an `A` somewhere. – Manuel Jan 09 '15 at 12:47
  • 1
    @Manuel class A is immutable though class B is not - I dont think immutability inferes that no subtype ever breaks immutability. Also if all fields are private (which they should be usually) you cannout change the internal state of the super type if no setters are presented – Patrick Jan 09 '15 at 12:49
  • @for3st immutability _does_ infers non extensibility. A is thread safe, yes. Immutable, no. – fge Jan 09 '15 at 12:52
  • It's just as a consumer you would have to expect mutable subclasses as well, if A is not final. So if you create a new instance of A, you know it's immuatble. But if A is passed as a (formal) parameter, you don't know if the (actual) instance is mutable or not. – Puce Jan 09 '15 at 12:53
  • @fge sorry pls show me where this is defined? On wikipedia & oracle docs it only states "An object is considered immutable if its state cannot change after it is constructed." - therfore my argument stands BigDecimal is immutable (as stated in javadoc) but not final. Also you can assure a consistent state with other methods than forbidding extensibility and still allow subtyping/inheritance – Patrick Jan 09 '15 at 12:55
  • @for3st if you extend the class and add a changeable member, then the state _can_ be changed after it is constructed! – fge Jan 09 '15 at 13:00
  • @fge That is true but it would be irrelevant to the immutable state of superclass A because even if you do A a = new B(); you could never access your B-specific state (without casting & reflection of course, but with that I can even change the state of class A). So in all: it is probably the safest way to declare a immutable class final, but I wont say it is an requirement to call it a immutable class – Patrick Jan 09 '15 at 13:02

6 Answers6

3

It's very simple: a class is immutable if whenever you create an instance of it,
you cannot change that instance's internal state/data. Whether you implement that
using final or some other mechanism is another question.

peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • It was my understanding that non-final classes are not immutable. E.g. from the java practices article: `It's interesting to note that BigDecimal is technically not immutable, since it's not final.` – Manuel Jan 09 '15 at 12:46
  • As it's not final, it means that you can override it and in that way start changing its internal data. So it's not immutable (technically). When viewed in that way, you can say that for a class to be immutable, it has to be final. But again, using the `final` is not the key here (is not the key to immutability). – peter.petrov Jan 09 '15 at 12:47
3

From the Effective Java 2nd edition, by Joshua Bloch:

To make a class immutable, follow these five rules:

  • Don’t provide any methods that modify the object’s state (known as mutators).

  • Ensure that the class can’t be extended. This prevents careless or malicious subclasses from compromising the immutable behavior of the class by behaving as if the object’s state has changed. Preventing subclassing is generally accomplished by making the class final, but there is an alternative.

  • Make all fields final.

  • Make all fields private.

  • Ensure exclusive access to any mutable components.

Points 1 and 3 through 5 are self-explanatory. The point 2 explains why allowing for class extension can affect your mutability (or not of). FWIW, the alternative he suggests in 2 is to make the constructors private, so no class can extend it (for a class to extend another, a call to the super constructor should be made, which cannot be done in this case) making it effectively non-extensible.

mystarrocks
  • 4,040
  • 2
  • 36
  • 61
2

Is Person immutable?

No it isn't.

An immutable class is final and only has final members.

In your case, what you want to use is a builder class:

final Person person = new PersonBuilder().withFather(xx).withMother(xx).build();

This way you can make all members of Person final, and since Person is itself final, you get a real immutable class.

fge
  • 119,121
  • 33
  • 254
  • 329
  • But what happens if I get cycles in the object graph? Is it possible to make such a class? – Manuel Jan 09 '15 at 12:48
  • That shouldn't be a problem; all the less if you use a builder class that you can check for anomalous conditions in the builder class itself. – fge Jan 09 '15 at 12:50
  • OK, this appears to be be the way to go. After some more searching, I found this: http://stackoverflow.com/a/4834584/84349 – Manuel Jan 09 '15 at 12:56
0

A Strategy for Defining Immutable Objects Java Doc

Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.

Java itself defines that you have to make class final to create immutable class.

atish shimpi
  • 4,873
  • 2
  • 32
  • 50
0

In Java, the final keyword prevents any subclasses of this class. So it is not possible for the subclass to define mutable fields by thereself. Your first example is immutable, since there are no methods to modify the values of the fields.

Beside that, in Java there are possibilities with Unsafe to modify even final fields, so there is no compiler-immutabibily like in other languages.

An immutable version of your Person class could look like this:

public final class Person {
  public final Person father;
  public final Person mother;
  public final String name;

  Person(final String name) {
    this(name, null, null);
  }

  Person(final String name, final Person father, final Person mother) {
    this.name = name;
    this.father = father;
    this.mother = mother;
  }

  public Person setFather(final Person father) {
    return new Person(name, father, this.mother);
  }

  public Person getFather() {
    return father;
  }

  public Person setMother(final Person mother) {
    return new Person(name, father, mother);
  }

  public Person getMother() {
    return mother;
  }

  public static void main(final String... args) {
    final Person p1 = new Person("Foo").setMother(new Person("Bar")).setFather(new Person("Baz"));
    System.out.println(p1);
  }

  @Override
  public String toString() {
    return "Person{" +
        "father=" + father +
        ", mother=" + mother +
        ", name='" + name + '\'' +
        '}';
  }
}
Martin Seeler
  • 6,874
  • 3
  • 33
  • 45
  • Marking Person class final does not make it immutable. Final class members make this class immutable. – Eric Sep 30 '21 at 09:04
0

A class can be immutable whether or not its properties are declared final if you do not provide any methods which modify the properties after construction then the class is immutable. Well, that's generally speaking but there are some issues to be aware of:

  • Obviously the properties need to be private to prevent them being changed by direct access or by subclassing for protected fields.

  • Depending on whether you have a security manager active it might still be possible to modify the properties using the reflections package to access them directly. Some tricks with serialization/deserialization might also allow unexpected things to happen.

  • Properties not declared as final may have concurrency issues - Java does not guarantee that threads other than the thread which creates the object will see the correct value of non-final properties unless synchronization is used.

  • Where the property references another object it may of course be possible that the internal state of the referenced object will change unless that too is immutable.

  • If the class is not declared final then it can be subclassed and getters overridden which may make it appear that properties have changed, although the original ones maintain their values.

BarrySW19
  • 3,759
  • 12
  • 26