This article provides an overview of how to create an immutable class in Java programming.
An object is immutable when its state doesn’t change after it has been initialized. For example, String
is an immutable class and, once instantiated, the value of a String
object never changes. Learn more about why the String
class is immutable in Java.
Because an immutable object can’t be updated, programs need to create a new object for every change of state. However, immutable objects also have the following benefits:
Learn more about multi-threading in Java and browse the Java Multi-Threading Interview Questions.
To create an immutable class in Java, you need to follow these general principles:
final
so it can’t be extended.private
so that direct access is not allowed.final
so that a field’s value can be assigned only once.The following class is an example that illustrates the basics of immutability. The FinalClassExample
class defines the fields and provides the constructor method that uses deep copy to initialize the object. The code in the main
method of the FinalClassExample.java
file tests the immutability of the object.
Create a new file called FinalClassExample.java
and copy in the following code:
Compile and run the program:
Note: You might get the following message when you compile the file: Note: FinalClassExample.java uses unchecked or unsafe operations
because the getter method is using an unchecked cast from HashMap<String,String>
to Object
. You can ignore the compiler warning for the purposes of this example.
You get the following output:
OutputPerforming Deep Copy for Object initialization
ce id: 10
ce name: original
ce testMap: {1=first, 2=second}
ce id after local variable change: 10
ce name after local variable change: original
ce testMap after local variable change: {1=first, 2=second}
ce testMap after changing variable from getter methods: {1=first, 2=second}
The output shows that the HashMap values didn’t change because the constructor uses deep copy and the getter function returns a clone of the original object.
You can make changes to the FinalClassExample.java
file to show what happens when you use shallow copy instead of deep copy and return the object insetad of a copy. The object is no longer immutable and can be changed. Make the following changes to the example file (or copy and paste from the code example):
return (HashMap<String, String>) testMap.clone();
and add return testMap;
.The example file should now look like this:
Compile and run the program:
You get the following output:
OutputPerforming Shallow Copy for Object initialization
ce id: 10
ce name: original
ce testMap: {1=first, 2=second}
ce id after local variable change: 10
ce name after local variable change: original
ce testMap after local variable change: {1=first, 2=second, 3=third}
ce testMap after changing variable from getter methods: {1=first, 2=second, 3=third, 4=new}
The output shows that the HashMap values got changed because the constructor method uses shallow copy there is a direct reference to the original object in the getter function.
You’ve learned some of the general principles to follow when you create immutable classes in Java, including the importance of deep copy. Continue your learning with more Java tutorials.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
… and here is a translation of the entire exercise into Scala, except without the wasteful copying and cloning, and with correct equals and hashCode methods: case class FinalClassExample(id: Int, name: String, testMap: Map[String,String])
- Ken
I didn’t understood what are you trying to point here?
- Pankaj
I belive that he was trying to point out that Scala lets you create immutable classes in more concise way. I’m new to Scala myself and already I love and often use these one liners. Less code less opportunities to make mistakes :)
- Paweł Chłopek
Thats a completely new thing to me… Got something new to learn now… will dig into this soon :)
- Pankaj
Shouldn’t it be “shallow copy” instead of “swallow copy” unless I am missing something?
- Shantanu Kumar
Thanks for pointing out the typo error. Corrected now to shallow copy.
- Pankaj
why don’t you just do this:
import static java.util.Collections.unmodifiableMap; public final class FinalClassExample { ... private final Map testMap; public FinalClassExample(int i, String n, Map m){ id = i; name = n; testMap = unmodifiableMap(new HashMap (m)); } public Map getTestMap() { return testMap; } ... }
- John
In this case, when we will get the testMap from getTestMap() function, we will not be allowed to modify it and it will throw exception. Also in that case again we are passing the reference and the values will change accordingly. Try executing this class:
package com.journeldev.java; import static java.util.Collections.unmodifiableMap; import java.util.HashMap; import java.util.Map; public final class FinalClassExample1 { private final Map testMap; public Map getTestMap() { return testMap; } public FinalClassExample1(Map hm) { this.testMap = unmodifiableMap(hm); } public static void main(String[] args) { HashMap h1 = new HashMap(); h1.put("1", "first"); h1.put("2", "second"); FinalClassExample1 ce = new FinalClassExample1(h1); System.out.println("ce testMap:" + ce.getTestMap()); h1.put("3", "third"); System.out.println("ce testMap after local variable change:"+ ce.getTestMap()); Map hmTest = ce.getTestMap(); hmTest.put("4", "new"); System.out.println("ce testMap after changing variable from accessor methods:"+ ce.getTestMap()); } }
Output will be: ce testMap:{2=second, 1=first} ce testMap after local variable change:{3=third, 2=second, 1=first} Exception in thread “main” java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableMap.put(Collections.java:1285) at com.journeldev.java.FinalClassExample1.main(FinalClassExample1.java:33)- Pankaj
Pankaj, You should not be allowed to modify a map you get from getTestMap. Objects coming from an immutable object, I would expect to also be immutable so you can not change the internals of the object after it is created. A mutable map may imply that the objects map is changed. Sure you can document that the object returned is new, but being defensive upfront seems better. Another alternative is to never return collections. Instead, define a forEach method that takes a function: public void forEach(Function fn) { for(Thing t : Iterable collection) { fn.apply(t); } }
- Steve
I am not sure why the map returned from getTestMap method should not be allowed to modify. Suppose the list contains some 1000 items and client wants to add 5 more to make their own immutable object instance, in this case its better to let them modify the returned object and do whatever they want on it. Usually, we have setter methods to change the state of object, so just by changing the returned object you should not think that the object state has been changed because the internal implementation is not known to us(as a client).
- Pankaj
you didn’t notice that I wrote:
this.testMap = modifiableMap(**new HashMap** (m)) `` so you couldn't change the inner map after construction. and I agree with Steve that the map returned by getTestMap() should not be modifiable as this would give the false impression that you're actually changing the immutable object. in that case I would prefer doing something like this: `FinalClassExample example = new FinalClassExample(...); Map newMap = new HashMap(example.getTestMap()); newMap.put("hello", "world");` ``
- John
Ok agreed that this will work but then its not generic. What if its some generic mutable class like Date or may be user defined class where you don’t have this feature. This example is intended to provide a generic way to create immutable classes. As for changing the object state, that is why we have setter methods and I still prefer it doing like that… but again it depends on your application requirements and design.
- Pankaj
In Groovy you can annotate the class as @Immutable and get /almost/ similar results to the scala example without all the boilerplate. IMHO Scala is better for it’s immutable support though. Also, don’t forget that Java Date, Dimension, and other JDK classes are not immutable as well, so you need to make defensive copies of those classes as well.
- Hamlet D’Arcy
Exactly, for all the mutable objects we need to return the defensive copy rather than same object reference. Have to dig into Scala now.
- Pankaj
Out of curiosity, why the requirement to have the class be marked as final so as not to be extended? What does disallowing subclasses actually provide in terms of allowing objects of this type to be immutable? Further, you don’t have to mark fields as private only just so long as you can guarantee that all constructor’s of the class properly initialize all of the fields. As a side note, you *can* have setters, but with the nuance that instead of changing an internal field, what the setter really does is specify a return type of the class the method is on, and then behind the scenes creates a new object using a constructor that accepts all internal fields, using the internally held state in for all params with the exception of the field represented by the setter called since you want the new object to have that field updated.
- whaley
If the class is not marked as final then its function can be overridden in the subclass either accidentally or intentionally. So its more related to keep the object secure. For this either all the getter methods can be made final or the class itself - this is again a design decision and depends on the requirement. Again if the fields wont be private then client application can override the value. Make the HashMap as public in the code and run the below code to see yourself.
FinalClassExample fce = new FinalClassExample(1,"", new HashMap()); System.out.println(fce.testMap); HashMap hm = fce.testMap; hm.put("1", "1"); System.out.println(fce.testMap);
Having a setter function will give the feeling that the actual object has been modified whereas internally creating a new object. Its better to client application know that its immutable (like String).- Pankaj
It’s super webpage, I was looking for something like this
- Łomża Zuhlke
Thanks mate, great details… – Anish Sneh
- Anish Sneh
Thanks for the kind words.
- Pankaj
Thanks, you know it and you know how to explain it too! I will definitely read more of your articles :)
- Mirey
Thanks for the detailed tutorial, well written and the flow goes exactely to showing up almost the need of every instruction in the code :) One side question, even if I know we are talking about Objects immutability,but what about the other instance variables you introucted in the
FinalClassExample
(id, name)? Is there any way to make them immutable?- Marwen
int and String both are already immutable, since there are no setter methods for them. For any other class variables, you should return a deep copy of the variable to avoid mutability.
- Pankaj
Should not be String name declared as a not final? Its not mutable anyway.
- Ramakant
Hi Pankaj, It was a great learning about creating Immutable objects.If you are performing step 5 and 6 then step 4 is not required I guess. You are not storing or returning original reference of HashMap, You are using clone concept for that, Hence as a result client application have no way to reassign new object to declared HaspMap. Please correct me If I am missing some thing Thanks& Regards Rais Alam
- Rais Alam
Can you please describe no 6. more deeply.I am not able to understand it.
- mahi
Excellent post!!
- S
thanx i disable ABP for u
- ahmed
Thanks Ahmed, I appreciate it.
- Pankaj
I can modify the object using ce.testMap.put(“10”, “ten”); Output: Performing Deep Copy for Object initialization true false ce id:10 ce name:original ce testMap:{2=second, 1=first} ce id after local variable change:10 ce name after local variable change:original ce testMap after local variable change:{2=second, 1=first, 10=ten} ce testMap after changing variable from accessor methods:{2=second, 1=first, 10=ten}
- Bijoy
how to overcome this case; ce.testMap.put(“10”, “ten”); //ce testMap after local variable change:{2=second, 1=first, 10=ten} //ce testMap after changing variable from accessor methods:{2=second, 1=first, 10=ten}
- parasuram tanguturu
testMap is private variable and out side class it wont be available. Its just a Example code that is why in main you are able to do ce.testMap but in real application you wont be as generally you dont do such operations in POJO class.
- JavaRocker
private final String name; …Why this is final …String is already final …do we need to declare it again…???
- Ajaz
“String is already final.” - yes , it is , but that final is at class level which means you can’t extend the String class, while that "private final String " is for variable , so that we cant change the value of that object once initialized.
- Vinay
Thank you Pankaj. I’m a big fan of your writing skills. You cover every details and explain concepts in easy to understand language. Thanks again.
- Vijay Nandwana
Very easy to understand and useful post!!
- Vineet kaushik
Thanks for great article! Can we use testMap.clone() in the constructor instead of going through all of the map items with Iterator?
- Bektur Toktosunov
Hi Pankaj I didn’t Understood the System.out.println(h1 == ce.getTestMap()) answer is False. Can you Please explain why it is false.
- WAA
On this statement we are just checking the reference of h1 is pointing to the one that of our final class TestMap reference, which in this case is no, because we have made a new copy of h1 hashmap and copied it in TempHashMap which is an completely new Object, & then the reference of this temp map is assign to TestHashMap. Hence this reference are pointing to, two different Object all together.
- Ankush K
hi pankaj, i love to read your blog. here i found a hack at main() using a reflection how to prevent it. Class mc = ce.getClass(); Field fs = mc.getDeclaredField(“name”); fs.setAccessible(true); fs.set(ce, “hacked”); System.out.println(“ce name after reflection hack:”+ce.getName());
- tabish
You cannot do this since name is final
- shashank
It does work.
- Umang Gupta
When you make the field final, Why making the variable private is mandate?
- Rahul
I it necessary to have variable as final?? We can achive it without it also, there is no statement to change varable.
- Pratyush
Hi setter method for map provides shallow copy only though clone method so that we can change the value later. How come you can say its immutable ?
- Anbu
I believe, you are talking about getter method. public HashMap getTestMap() { //return testMap; return (HashMap) testMap.clone(); } I believe, It should return a copy by deep cloning.
- Prakash
I believe, In this example, Shallow copy would also work fine as long as we are storing immutable String object in HashMap. In case, if we need to store mutable object in HashMap. We should do deep cloning
- Prakash
Which Java version ? or forgot the Final variables initialisation?
- Tanmai
Final variables can be left uninitialized in declaration if initialization is provided by constructor.
- Manoj Kumar Vohra
Correct output is:: Performing Shallow Copy for Object initialization true false ce id:10 ce name:original ce testMap:{2=second, 1=first} ce id after local variable change:10 ce name after local variable change:original ce testMap after local variable change:{3=third, 2=second, 1=first} ce testMap after changing variable from accessor methods:{3=third, 2=second, 1=f irst}
- Rushabh
I am not clear on Point 5 initialization swallow or Deep are comparison not initialization. swallow comparison is done by == and Deep comparison by equal / equalignorecase Did you mean initialization should be done at constructor with safer way without exposing identity of the fields?
- kuldeep patil
I also wrote an article with a complete different view and you may have a look, https://www.codedjava.com/2017/09/immutable-objects-in-java\_50.html Thanks,
- Sameera
What if we remove the final access modifier from class as we are independently handling all the fields or methods of this class. And if some one extend this class then they doesn’t impact this class instance. Please suggest.
- Vaibhav Jetly
But then it is circumventing the intent of the class. If you want additional behavior from a different class, create a different class and have the immutable one as an instance member of that class.
- Mike New
Your getter method for HashMap can be in that way public HashMap getTestMap() { //return testMap; //HashMap tempMap=new HashMap(); return new HashMap(testMap); }
- TEJENDRA PRATAP SINGH
correct output is: Performing Shallow Copy for Object initialization true false ce id:10 ce name:original ce testMap:{1=first, 2=second} ce id after local variable change:10 ce name after local variable change:original ce testMap after local variable change:{1=first, 2=second, 3=third} ce testMap after changing variable from accessor methods:{1=first, 2=second, 3=third}
- Hagos haile
Yeah Thats what i was thinking .ce testMap after changing variable from accessor methods:{1=first, 2=second, 3=third} 4=new wont be get added
- Vigilante
Hi Pankaj, What will happen if we do not declare class as final, since member variables are privare so can not be extendable. I am here trying to understand why we need final for class and its member variable. Is there any way state of a class can be modified if we don’t declare class and its variables final? Thanks, Prahlad.
- Prahlad Kumar
You need the class as final so, that it cannot be extended further and it’s implememtation should not be changed by overriding the methods and by creating new variables. Making variables as final will make sure two things, 1. Compiler will flag error if any reassignment will take place 2. It will give readability to the code to other people who are working on the same class that these variables are not supposed to be changed. Basically it is because of IMHO
- Gunjal Shrivastava
But if the variables are private and we do not provide any setter methods, how will their values ever change once initialized? Reassignment is not possible in this case. Making the class final is also not necessary. What method can a subclass override that will change the private member variables of the parent class? Is there something I am missing?
- Vaibhav Souveer
Hi Pankaj, Thanks for writing such an informative article. I would like to know what if my all member variables are immutable like all Strings or Wrappers?, Do I still need to follow above steps? Thanks,
- Jitendra Singh
Yes, most of them. You can get rid of deep-copy logic if the variables are immutable.
- Pankaj
Great Article… very helpful in cracking interviews … Thank you so much !!
- Omkar J
Article is very nice and easy to understand. I have gone through the comments and got deeper understanding of this concept. Requesting everyone else to go through the discussions done in comments then you will get more understanding about this topic. Thanks to Pankaj.
- Vijay Kumar
This is how we create an immutable class. But can you please describe how the immutable object is created? or is that happens in java or not??
- u.mang
A class is a design and Object is the actual implementation. When we say immutable class, it means immutable objects too.
- Pankaj
What happens when we have an inner class. How to handle that situation?
- janny
Not required map iteration in constructor and return statement clone which you used, may be any other reason please provide that. class Student { private final int sid; private final String sname; private final Map map; public Student(int sid, String sname, Map map) { super(); this.sid = sid; this.sname = sname; this.map = new HashMap(map); //this.list=list; } public int getSid() { return sid; } public String getSname() { return sname; } public Map getList() { return map; } @Override public String toString() { return “Student [sid=” + sid + “, sname=” + sname + “]”; } }
- Parvise
Yes, we can use the HashMap constructor too. However, the idea here was to showcase how to perform the deep copy. Not every object constructor provides this feature, in that case, we need to perform field by field copy so that the original object and the cloned object have different references.
- Pankaj
Hi, please post your Java code of connection pooling in java without using Spring and hibernate.
- Deepak
It was worth a read about immutable class in Java! Really learned a lot form this article. I can imagine the effort you put into this & especially appreciate you sharing it. keep up the good work.
- online java training
Hi, First i want to thank you for sharing your knowledge with us. here i have one question with this if i am getting HashMap reference through getter method then i can avoid changing Immutable class value by sending deep copy. But what if user accessed it directly? i mean Map local = ce.testMap; local.put(“local” , “local”); in this case ce.testMap will have the above added object as well. How can we avoid this? thanks!
- Pandidurai
while returning map return new Object. So the getter method of testMap will look like getTestMap{ return new HashMap(testMap); }
- Rishabh
Hi Pandidurai , testMap access is private as rule, so you can not access outside class directly, without getter you can not access directly
- vkas
Even within this constructor if you assign cloned version of input “hm” to testMap, it should still work, no need of deep copy. public FinalClassExample(int i, String n, HashMap hm){ System.out.println(“Performing Shallow Copy for Object initialization”); this.id=i; this.name=n; this.testMap=hm.clone(); }
- Ravi Beli
Perfect answer with a small change. You need to type cast when calling the clone method as it return object.
- Satish
What’s the significance of declaring class as final here?
- Ashwani Pratap
So that it can’t be extended.
- Pankaj
But when we trying to change a String object a new object would be created with those changes. Is that possible here ?. Please explain
- Sreerag
One of the best example and explanation for “creating immutable class”. Loved it. Thanks !!
- Mukund Padale
4. Make all mutable fields final so that it’s value can be assigned only once. I don’t think this is necessary if are declaring the fields private. mutable fields marked as final can still be mutated, only the reference can not be changed.
- Manohar Bhat
Afaik private modifier doesn’t help against reflection. Final does
- Bartosz Wyględacz
What about Date variable we can still change the date by writing d.getTime(); How can we avoid that?
- jogi
public final class Sample { private final Date dateField; //Default private constructor will ensure no unplanned construction of class private Sample(Date date) { this.dateField = new Date(date.getTime()); } /** * Date class is mutable, so we need a little care here. * We should not return the reference of original instance variable. * Instead of a new Date object, with content copied to it, should be returned. * */ public Date getDateField() { return new Date(dateField.getTime()); } }
- Jerome
Hi Pankaj, I have been following your site for quite some time & it has been helping me a lot… Thank you very much for that. Just one suggestion, the “scrollable” code blocks are not very user-friendly to use. It would be great if we can see the entire code at once - even though it increases the height of the page, just my personal experience…
- Shobhit Mittal
hi Pankaj, great work !!! what is the need of getter method HashMap clone as we are constructing deep clone object? thanks Aravind Sundarraj
- Aravind Sundarraj
Hi Aravind , If we don’t do a getter method Clone , then we will be sending the Original object reference without making any copy in the memory . So that original object reference can be easily modified after fetching it from getter method. If you return the HashMap like below , even after deep clone in constructor that we can modify the original object public HashMap getTestMap() { return testMap; } System.out.println(“ce testMap:”+ce.getTestMap()); h= ce.getTestMap(); h.put(“3”,“three”); System.out.println("ce testMap after accessor method cahnges "+ce.getTestMap());
- Divya
Hi, its Explained very nicely, I have one question like i have 2 classes as below. class Separtment{ int id, String name; } Class Employee{ Department Dept, int id, String name; } Here i want to make Employee as immutable, but here Department is a child object, so here how come i make Employee class is immutable. Also One constraint like the Department object which I won’t own(I am not allowed to change anything in the Department object).
- Akhil Kumar Patro
As shown in example, first you must add modifiers(private & final) for every variable. While adding getDept(); you must return clone() object as shown in example as testMap. while writing constructor instead of assigning directly take a temporary dept variable and populate and assign to the main this.dept variable(not direct variable, dublicate variable you have to do).
- Kalyan Gudla
HI Pankaj, I tried your code and analysed that by only returning the map.clone() object will work here no need to make deep copy in constructor.
- Lokesh Kumar
Hi Lokesh, If you don’t make a deep copy in the constructor and just do a map.clone() alone , then the changes for local map in the main method will affect the Final class map . Try this with Shallow copy , you will understand better public static void main(String[] args) { HashMap h1 = new HashMap(); h1.put(“1”, “first”); h1.put(“2”, “second”); String s = “original”; int i=10; FinalClassExample ce = new FinalClassExample(i,s,h1); System.out.println(“ce testMap:”+ce.getTestMap()); h1.put(“3”,“Third”); System.out.println(“ce testMap after local variable change:”+ce.getTestMap()); }
- Divya
I believe deep copy will be needed for ID and Name as well. Please explain if not.
- Dinesh Solanki
who can solve this for me? /* Edit the code by following the steps in the description */ import java.util.HashMap; import java.util.Map; public class Location { final int locationID; public String description; protected Map exits; public Location(int locationID, String description, Map exits) { this.locationID = locationID; this.description = description; this.exits = new HashMap(exits); this.exits.put(“Q”, 0); } public void addExit(String direction, int location) { exits.put(direction, location); } public void setLocationID(int locationID) { this.locationID = locationID; } public int getLocationID() { return locationID; } public void setDescription(String description) { this.description = description; } public String getDescription() { return description; } public void setExits(Map exits) { this.exits = exits; } public Map getExits() { return exits; } } Make the Location class an Immutable Class. The strategy for creating an Immutable Class is: Steps: 1. Don’t provide setters. 2. Make all fields final and private 3. Don’t allow the class to be subclassed. 4. If the instance fields include references to mutable objects, don’t allow those objects to be changed: - Don’t provide methods that modify the mutable objects. - Don’t share references to the mutable objects. As an added Task, handle the case where exits is null when passed to the constructor. NOTE: Not all classes documented as “immutable” follow these rules. However, the steps above are the basis of an Immutable Class.
- Francis