简体   繁体   中英

Immutability in Java

If my class is going to be an immutable one it has to be final and without any method that modifies its state and all properties must be private. But why should it have properties declared as final (for example, private final int a )?

Edit

Will the class still be an immutable one if it has a reference to objects that are not immutable?

如果将私有成员变量标记为final ,则需要使用编译器和运行时引擎来确保其值永远不会被类本身或其子类修改。

A final class is one that cannot be subclassed. The final keyword on a class definition does not necessarily make the class immutable. So your class does not have to be final for instances of it to be immutable (your question implies that you are making the class final, note that final can be used to modify more than just classes). However, if you make a class final that class cannot be subclassed, so you are guaranteed that no mutable subclasses can be created.

Immutability simply means that no values on the class can be changed. So private fields, no non-private setters should do it. One common issu is if your class has a Collection, you have to return some sort of unmodifiable collection, else the values in the collection can be changed, even though noone could change the reference to the collection. This answers the second part of your question. If declare a final myCollection on your class, you can't change the reference to myCollection, but you can still modify the collection outside of the class, if you have a getter (which is fine) and don't return an unmodifiable collection. See http://download.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableCollection(java.util.Collection )

With respect to immutability, keep in mind that the goal is to make it impossible to change instances of a class, once the values are initially set. Using the final keyword can help, but is not by itself sufficient to have an immutable definition.

From Jeremy Manson's blog post Immutability in Java :

Now, in common parlance, immutability means "does not change". Immutability doesn't mean "does not change" in Java. It means "is transitively reachable from a final field, has not changed since the final field was set, and a reference to the object containing the final field did not escape the constructor".

Manson co-authored the Java Memory Model spec, so should be knowing what he is talking about.

An example from the Brian Goetz's Java Concurrency in Practice book:

public Holder holder;
public void initialize() { holder = new Holder(42); }

publc class Holder {
  private int n;
  public Holder(int n) { this.n = n; }

  public void assertSanity() {
    if (n != n) {
      throw new AssertionError(”This statement is false”);
    }
  }
}

// Thread 1:             // Thread 2:
initialize();            if (holder != null) holder.assertSanity();

Thread 2 above can throw the AssertionError . To avoid such visibility issues the field n of Holder class should have been declared final .

EDIT: Consider the sequence of steps:

  1. Allocate memory for new object of type Holder
  2. Initialize field n of the new object
  3. Execute the constructor
  4. Assign the reference of the new object to holder reference

This is the "sane" sequence of events and the JMM (Java Memory Model) guarantees that this is what the thread calling initialize() will see. But, since there is no synchronization, JMM makes no guarantees about what sequence the second thread calling holder.assertSanity() will see. In particular, the AssertionError will be thrown if the second thread sees the sequence:

  1. holder is non-null, so that it could call assertSanity method
  2. The first read of n results in 0, the default value of an int field
  3. The second read of n results 42

In short, when an object's constructor returns in one thread, another thread might not see all field initializations of that object. To guarantee that the second thread sees the correctly initialized fields, you should either make them final , or do a mutex unlock (ie initialize() method should be synchronized ) or do a volatile write (ie holder should be volatile ).

It is not strictly required that the properties of an immutable class are final. If the immutable class is itself final, and you take care not to change the properties after construction, it is legal to not have those properties final.

There is however no reason to have them not final. Making them final allows the compiler to check your code and prevent accidental modification. It also documents the fact that you don't want those properties to be written more than once (on creation).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM