71

My goal is to make a Java object immutable. I have a class Student. I coded it in the following way to achieve immutability:

public final class Student {

private String name;
private String age;

public Student(String name, String age) {
    this.name = name;
    this.age = age;
}

public String getName() {
    return name;
}

public String getAge() {
    return age;
}

}

My question is, what is the best way to achieve immutability for the Student class?

Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
Ruchira Gayan Ranaweera
  • 34,993
  • 17
  • 75
  • 115
  • 3
    From the Java tutorials: [A Strategy for Defining Immutable Objects](http://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html) – Ted Hopp Aug 12 '13 at 18:40
  • Possible duplicate of [How to create immutable objects in Java?](https://stackoverflow.com/questions/6305752/how-to-create-immutable-objects-in-java) – roottraveller Sep 25 '17 at 10:32
  • Java SE 16 makes it amazingly easier to create an immutable class. https://stackoverflow.com/a/65976915/10819573 – Arvind Kumar Avinash Jan 31 '21 at 08:26

16 Answers16

71

Your class is not immutable strictly speaking, it is only effectively immutable. To make it immutable, you need to use final:

private final String name;
private final String age;

Although the difference might seem subtle, it can make a significant difference in a multi-threaded context. An immutable class is inherently thread-safe, an effectively immutable class is thread safe only if it is safely published.

Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
  • What would be the correct pattern for an immutable class which encapsulates an instance of a mutable class, but will never allow that instance to be exposed to any code which might mutate it once the constructor returns? For example, a `final` field of `foo` holds an array `arr` and the last thing the constructor does is store a 5 into element 3. If one thread does `blah=new foo()` and another thread accesses `blah.arr[5]`, I understand the store of array reference to `arr` is guaranteed to be occur before the second thread sees the write to `blah`, but what about the write to `arr[5]`? – supercat Aug 19 '13 at 16:27
  • @supercat not 100% sure to be honest. You should ask the question. I think a safe pattern would be to populate a temporary array then assign it to your final variable. But if you populate the final variable directly I'm not sure. – assylias Aug 19 '13 at 16:54
  • @supercat make the instance of the mutable class private and final, also make any method that return that instance return new copy of the object – Melad Basilius Jul 19 '16 at 09:16
  • @MeladEzzat: Since writing the above, I've read the spec more and in cases where the field can sensibly be `final`, Java provides the necessary semantics. As of Java 7, however, I couldn't find any way to handle lazily-generated immutable data without imposing an extra level of indirection or locking on consumers. From a machine perspective, it should be possible to place all the burden on the class which lazily generates the data, since no consumer thread could possibly have the array in cache before it's created, so if the consuming thread forces the array out to main memory... – supercat Jul 19 '16 at 14:00
  • ...before any consumer thread could possibly see it, there should be no way for a consumer to see stale data. Unfortunately, I don't know any way to establish the proper memory barrier on the lazy-creation thread; having a lock on the writer thread would "almost" certainly work, but the JIT would be allowed to break it. – supercat Jul 19 '16 at 14:05
60

There are few things that you must consider for making an immutable class:

  • Make your class final - You already have
  • Make all the fields private and final - Make appropriate changes in your code
  • Don't provide any methods that change the state of your instance
  • If you have mutable fields in your class, like List, or Date, making them final won't suffice. You should return a defensive copy from their getters, so that their state isn't mutated by calling methods.

For the 4th point, say you have a Date field in your class, then the getter for that field should look like:

public Date getDate() {
    return new Date(this.date.getTime());
}

Making a defensive copy can become a headache, when your mutable field itself comprises of some mutable field, and that in turn can contain some other mutable field. In that case, you would need to make copy of each of them iteratively. We name this iterative copy of mutable fields as Deep Copy.

Implementing deep copy by yourself may be cumbersome. But,keeping that issue apart, you should consider your class design again, once you see yourself falling into such requirement of making deep defensive copy.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • 1
    *"Don't provide any methods that change the state of your instance"* I don't think this is a requirement. As long as the state change is not visible to the outside world, it will be fine. – arshajii Aug 12 '13 at 18:42
  • @arshajii. Can you give an example where state change in field is not visible outside? – Rohit Jain Aug 12 '13 at 18:44
  • 1
    If you have a private field that nobody else can see. – arshajii Aug 12 '13 at 18:45
  • @arshajii. But there of course are getters for that field? Which will return the modified state. – Rohit Jain Aug 12 '13 at 18:46
  • 1
    I mean if there are no getters/setters, just a field to be used by the object itself. Maybe our definitions of 'state change' are different; would you consider changing such a field to be a state change? – arshajii Aug 12 '13 at 18:47
  • 3
    @arshajii - If internal state of an object can change, the object is not immutable. If the internal state enters into the logic of `equals` or `hashCode`, then the internal state is (indirectly) visible to the outside world. – Ted Hopp Aug 12 '13 at 18:48
  • @TedHopp `String`, for example, has a non-final field `hash` that can change. – arshajii Aug 12 '13 at 18:49
  • @arshajii. The field `hash` is used for caching the hashcode of strings, so that it doesn't need to be computed always. If `hash` changes, that means the `hashcode` itself is changed. In which case, you have a different instance of the `String`. – Rohit Jain Aug 12 '13 at 18:51
  • `hash` can change once when you first call `hashCode`, I would argue that this is an internal state change. – arshajii Aug 12 '13 at 18:53
  • 1
    @arshajii. Just went through the source code. Seems like you're right. And now I'm confused. – Rohit Jain Aug 12 '13 at 19:00
  • @arshajii. In some cases, it can calculate the hash only when the `hashcode` method is called. – Rohit Jain Aug 12 '13 at 19:02
  • A defensive copy isn't even necessarily enough -- it has to be a _deep_ defensive copy. For instance, creating a copy like `new ArrayList(incomingDatesList)` isn't enough because, though you've defended against the incoming list being changed, you haven't defended against its members changing. Unfortunately, there isn't a great generalized solution to this in Java; you have to know what types you're defensively copying and handle them appropriately. – yshavit Aug 12 '13 at 19:09
  • @yshavit. Well, I actually meant that only. Will edit the answer to make it more clear. – Rohit Jain Aug 12 '13 at 19:10
  • 1
    @arshajii String#hash is a very specific example which works because of careful reasoning about visibility - it is not a recommended practice. – assylias Aug 12 '13 at 19:53
  • +1 to String#hash being an exception. It's not just mutable state in an immutable class, it's also a purposeful data race. They should really throw a comment on there to the effect of `// don't cite this technique unless you can quote the JMM from memory` – yshavit Aug 12 '13 at 20:59
  • I was reading about immutability on Wikipedia and came across this: *"In some cases, an object is considered immutable even if some internally-used attributes change but the object's state appears to be unchanging from an external point of view."*. – arshajii Aug 13 '13 at 16:42
  • @yshavit: The `string.hash` case isn't really a data race because no code that reads from `hash` will have any of its post-conditions affected by the value read. Even if the effects of writes to the `hash` variable were arbitrarily delayed by massive amounts of time (e.g. even after its written, reads continue to report the old value for the next three days), the only effect on program execution would be an increase in execution time; outside contrived scenarios the increase would be very slight. – supercat Aug 19 '13 at 16:43
  • @supercat It _is_ a data race, because a field is being read and written to by multiple threads without any synchronization between those accesses. See [JLS 17.4.5](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5): `When a program contains two conflicting accesses (§17.4.1) that are not ordered by a happens-before relationship, it is said to contain a data race.` and 17.4.1 `Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write.` It's carefully constructed to be benign, but it's still a data race. – yshavit Aug 19 '13 at 16:48
  • @yshavit: What terminology would you use to distinguish the case where a data race would cause a methods to have different post-conditions depending upon who wins, all possible post-conditions would meet spec, from the scenario that exists in `hashCode`, whose post-conditions are *completely unaffected*? I'd call the former a "benign data race"; the latter would seem to represent a substantially different scenario, worthy of a different term. Perhaps "trivial data race"? – supercat Aug 19 '13 at 17:00
  • @supercat I guess I would call both benign, but I don't really know. It's all just labels; you could call one of them a Banana Data Race for all I'd care... :) I think that an intentional data race is something dangerous enough that maybe it'd make sense to not subcategorize them, but treat each situation separately. So rather than saying that `String.hash` is a trivial data race or whatever, spell it out fully: "it's an intended data race which is safe because..." – yshavit Aug 19 '13 at 17:08
  • @yshavit: For the `hash` variable, one need only show two things: (1) for a given instance, `hash` can only change once; (2) code will behave identically whether or not that change has occurred--something that can be observed by examining single-threaded behavior. That's massively simpler than some other "benign data race" scenarios where the number of significantly-different operation sequences one must consider is limited only by the number of simultaneous threads (requiring the use of induction or similar techniques to show that invariants always hold through all of them). – supercat Aug 19 '13 at 17:28
  • #1 of those is not strictly true. It's possible for `hash` to change several times, once on each thread. But putting that aside, my point was that a data race is a subtle and complex enough issue that it's probably best if each one is individually reasoned out. Btw, can you cite any examples of what you term a benign, non-trivial data race? – yshavit Aug 19 '13 at 17:32
  • @yshavit: The scenario where thread #1 changes `hash` from zero to the computed value, and thread #2 does likewise before the change percolates to thread #2 would be indistinguishable (except by monitoring electrical signals connecting the caches) from the scenario where thread #1's change percolates to thread #2 just before thread #2's attempted write. As for a benign data race, such things can occur with e.g. lazy computations that generate immutable objects rather than filling in primitive values. Since it can be *much* faster to compare references than deep-compare objects, ... – supercat Aug 20 '13 at 15:13
  • ...one may want to ensure that references to distinct-but-identical objects will eventually get replaced with references to the same object, but the use of locking primitives to ensure such references get merged instantly may be a bigger performance drain than would be accepting the possibility that one thread might sometimes continue to use two distinct instances even though another has recognized that they can be merged. – supercat Aug 20 '13 at 15:19
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/35852/discussion-between-yshavit-and-supercat) – yshavit Aug 20 '13 at 15:44
  • Instead of custom deep cloning implementation, mark every custom class inside the root class as Serializable and then use org.apache.commons.lang3 SerializationUtils.clone(rootObject) – Praveen Kamath Nov 04 '18 at 21:00
7

How do you make a mutable object immutable?

  1. Declare the class as final so it can’t be extended.
  2. Make all fields private so that direct access is not allowed.
  3. Don’t provide setter methods for variables
  4. Make all mutable fields final so that it’s value can be assigned only once.
  5. Initialize all the fields via a constructor performing deep copy.
  6. Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.

source

Why do we create immutable objects?
Immutable objects are simply objects whose state (the object's data) cannot change after construction.

  • Security: store sensitive pieces of information like usernames, passwords, connection URLs, network connections etc.
  • are simple to construct, test, and use
  • are automatically thread-safe and have no synchronization issues
  • don't need a copy constructor
  • don't need an implementation of clone
  • allow hashCode to use lazy initialization, and to cache its return value
  • don't need to be copied defensively when used as a field
  • make good Map keys and Set elements (these objects must not change state while in the collection)
  • have their class invariant established once upon construction, and it never needs to be checked again
  • always have "failure atomicity" (a term used by Joshua Bloch): if an immutable object throws an exception, it's never left in an undesirable or indeterminate state

Source

In Java, Strings are immutable, which provides, such as caching, security, easy reuse without replication, etc. Source

Premraj
  • 72,055
  • 26
  • 237
  • 180
3

With final keyword:

private final String name;
private final String age;
Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
3

Making variables private and no setter methods will work for primitive data types. If my class has any collection of objects?

To making any class immutable with collection object?

Write your own collection object with extends collection class and follow the private variables and no setter methods. or return clone object of your collection object.

public final class Student {

private StudentList names;//Which is extended from arraylist

public Student() {
names = DAO.getNamesList()//Which will return All Student names from Database  its upto you how you want to implement.
}

public StudentList getStudentList(){
return names;//you need to implement your own methods in StudentList class to iterate your arraylist; or you can return Enumeration object.
}

public Enumeration getStudentNamesIterator(
Enumeration e = Collections.enumeration(names);
return e;
}

public class StudentList extends ArrayList {

}
2

This is fine but I would make the fields final as well.

Also I would make the age an int or double rather than a String.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
2

Expanding on the answer a bit.

final is not the same as Immutable but you can use final to make certain things immutable if you use it in certain ways.

Certain types are immutable, in that they represent unchanging values rather than objects of changeable state. Strings, numbers, etc are immutable. At the end, usually our objects boil down to data structures eventually referencing immutable values, but we change the data structures by assigning new values to the same field names.

So to make something truly immutable you need to make sure that final is used all the way down, until you reach every field reaching every value at the base of your composition tree. Otherwise something could change out from under your the object and it isn't really fully immutable.

Chris Travers
  • 25,424
  • 6
  • 65
  • 182
1

Your example is already immutable object, because fields in Student class can only set on instance initialization.

To make object immutable, You must do these steps:

  1. Don't use any methods, which can change fields of your class. For example don't use Setters.
  2. Avoid to use public non-final fields. If your fields is public then you must declare them as final and initialize them in constructor or directly in the declaration line.
Eldar Agalarov
  • 4,849
  • 4
  • 30
  • 38
1

It is too late to answer but may be it help other peoples who have this question.

  1. State of immutable object can not be modified after construction, any modification should result in new immutable object.
  2. All fields of Immutable class should be final.
  3. Object must be properly constructed i.e. object reference must not leak during construction process.
  4. Object should be final in order to restrict sub-class for altering immutability of parent class.

I think this link help more Read more: http://javarevisited.blogspot.com/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html#ixzz40VDQDDL1

Jérémie B
  • 10,611
  • 1
  • 26
  • 43
Yirga
  • 881
  • 1
  • 12
  • 31
0

It already is immutable -- you can't change the contents once you initialize it, since you haven't made setters. You might add final keywords to the variables.

Nick C
  • 514
  • 3
  • 12
0

Making all variables as final and when setting some field, making it return the reference to the new Student object with the newly set value like in String.

ps-aux
  • 11,627
  • 25
  • 81
  • 128
0

You can just follow guidelines shown in this example (first result in google): http://www.javapractices.com/topic/TopicAction.do?Id=29

JPetric
  • 3,838
  • 28
  • 26
0

Here are few rules, which helps to make a class immutable in Java : 1. State of immutable object can not be modified after construction, any modification should result in new immutable object. 2. All fields of Immutable class should be final. 3. Object must be properly constructed i.e. object reference must not leak during construction process. 4. Object should be final in order to restrict sub-class for altering immutability of parent class.

Example:

public final class Contacts {

private final String name;
private final String mobile;

public Contacts(String name, String mobile) {
    this.name = name;
    this.mobile = mobile;
}

public String getName(){
    return name;
}

public String getMobile(){
    return mobile;
}

}

Refer this link : http://javarevisited.blogspot.in/2013/03/how-to-create-immutable-class-object-java-example-tutorial.html

Vijay Bhatt
  • 1,351
  • 13
  • 13
0

According to Strategy for Defining Immutable Objects

  1. Don't provide "setter" methods — methods that modify fields or objects referred to by fields.

  2. Make all fields final and private.

  3. Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final.

    a. A more sophisticated approach is to make the constructor private and construct instances in factory methods.

  4. If the instance fields include references to mutable objects, don't allow those objects to be changed:

    a. Don't provide methods that modify the mutable objects.

    b. Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

YourAboutMeIsBlank
  • 1,787
  • 3
  • 18
  • 27
0

Java SE 16

You can use JEP 395: Records feature, introduced as part of Java SE 16, to create an immutable class in a succinct manner.

If you have already gone through the above link, you must have figured out that you can do it simply as

record Student(String name, String age) { }

What you get in turn are:

  1. A final class Student.
  2. A canonical constructor whose signature is the same as the header, Student(String name, String age).
  3. private final fields, name and age and their corresponding public accessor method with the same name and return type.
  4. Automatically created equals, hashCode and toString methods.

Demo:

Student.java

record Student(String name, String age) { }

Main.java

class Main{
    public static void main(String[] args) {
        Student s1 = new Student("Bharat", "10 Years");
        Student s2 = new Student("Arvind", "10 Years");

        System.out.println(s1);
        System.out.println(s1.equals(s2));
        System.out.println(s1.age().equals(s2.age()));
    }
}

Output:

Student[name=Bharat, age=10 Years]
false
true
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
-2

Make the class or variable as final that's more than enough

Public final class constants
{
  private final String name;
  private final String mobile;

  // code
}
zessx
  • 68,042
  • 28
  • 135
  • 158