问题
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;
}
}
回答1:
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.
回答2:
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.
回答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.
回答4:
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.
回答5:
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 + '\'' +
'}';
}
}
回答6:
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.
来源:https://stackoverflow.com/questions/27860850/immutable-java-class-with-non-final-member