Make immutable Java object
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.
Proper way to create immutable objects in Java
I guess there is no easier way than something like this (lombok), but this is not plane java so...
@Builder(toBuilder = true)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
final class SimpleObservable{
private final SimpleObserver observer;
private final List<String> canChange;
public SimpleObservable(List<String> canChangeSet){
canChange = new ArrayList<>(canChangeSet);
observer = SimpleObserver.builder()
.name(canChangeSet.get(0))
.build();
}
public SimpleObservable changeCanChange(List<String> canChangeSet){
SimpleObservableBuilder sob = this.toBuilder();
sob.canChange(new ArrayList<>(canChangeSet));
sob.observer(observer.notifyMe(sob.canChange));
return sob.build();
}
}
@Builder(toBuilder = true)
final class SimpleObserver{
private final String name;
public SimpleObserver notifyMe(List<String> changed){
SimpleObserverBuilder sob = this.toBuilder();
sob.name(changed.get(0));
return sob.build();
}
}
How to create immutable object from mutable in java?
tl;dr
Either:
- Make a
record
like this, in Java 16 and later:public record Planet( String name , LocalDate discovered ) {}
- Or, before Java 16, make a class where you:
- Mark all member fields
final
andprivate
. - Make getter methods as needed, but no setter methods.
- Mark all member fields
Record
Just use the new records feature in Java 16 (previewed in Java 15).
Define your class as a record
when its main job is to transparently and immutably carry data. The compiler implicitly creates a constructor, the getters, hashCode
& equals
, and toString
.
Notice that the getter methods implicitly defined in a record do not begin with the JavaBeans-style get…
wording. The getter method is simply the name of member field as defined in the parentheses following the class name.
Of course, if your getter methods provide access to an object that is itself mutable, being contained in a record does nothing to stop the calling programmer from mutating the contained object. Notice in the example class next that both String
and LocalDate
classes are themselves immutable by design. So the mutability of a contained object is a non-issue here.
package org.example;
import java.time.LocalDate;
public record Planet( String name , LocalDate discovered )
{
}
Using that record.
Planet Earth = new Planet( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );
System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.name: " + Earth.name() );
System.out.println( "Earth.discovered: " + Earth.discovered() );
When run.
Earth
------------------------------------
Earth.name: Earth
Earth.discovered: 2020-01-16
Class
Without the records feature, to make sure a class is immutable you should:
- Mark the member fields
final
. This means the field cannot be assigned a different object after the constructor has finished. - Mark the member fields
private
. This means objects of other classes will not have direct access to read or change those fields. - Provide getter methods, if needed, but no setter methods. By convention, the JavaBeans-style
get…
oris…
naming is used.
You should also provide appropriate override implementations of hashCode
, equals
, and toString
. Your IDE will help generate the source code for those.
package org.example;
import java.time.LocalDate;
import java.util.Objects;
public class Planète
{
// Member fields
final String name;
final LocalDate discovered;
// Constructors
public Planète ( String name , LocalDate discovered )
{
Objects.requireNonNull( name );
Objects.requireNonNull( discovered );
this.name = name;
this.discovered = discovered;
}
// Getters (read-only immutable class, no setters)
public String getName ( ) { return this.name; }
public LocalDate getDiscovered ( ) { return this.discovered; }
// Object class overrides
@Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Planète planète = ( Planète ) o;
return getName().equals( planète.getName() ) && getDiscovered().equals( planète.getDiscovered() );
}
@Override
public int hashCode ( )
{
return Objects.hash( getName() , getDiscovered() );
}
@Override
public String toString ( )
{
return "Planète{ " +
"name='" + name + '\'' +
" | discovered=" + discovered +
" }";
}
}
Using that class.
Planète Earth = new Planète( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );
System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.getName: " + Earth.getName() );
System.out.println( "Earth.getDiscoveryDate: " + Earth.getDiscovered() );
Side issues
Do not start a decimal integer literal with 0
. The leading zero makes the number octal rather decimal. So your code passing 2020,01,16
should be 2020,1,16
.
Never use the Date
class, nor Calendar
or SimpleDateFormat
. These terrible classes are now legacy, supplanted years ago by the modern java.time classes defined in JSR 310. In code above, we used java.time.LocalDate
to represent a date-only value, without a time-of-day and without a time zone.
Is it possible to make an object immutable after it is created
An immutable is an unchangeable object, which means that once it's created there is no way to change its fields. Basically, you have to omit all the setters and declare fields as final
. Declaring the class as final
ensures that none can inherit from it.
public final class Car {
private final List<Door> doors;
private final Engine engine;
public Car(final Engine engine, final List<Door> doors) {
this.engine = engine;
this.doors = doors;
}
// Getters ONLY
}
This example ensures the class is not only immutable but also constant
. I highly recommend you to read an article about the Gradients of Immutability.
How to implement immutable and read-only versions of mutable objects effectively?
is this safe or is it somehow possible to mutate
DataHolder
leveraging access to this reference? If yes, how ?
Since getData
returns a String
, which is immutable, the caller can't change anything through the returned reference. You can't access a DataHolder
through a String
either. Why would String
know about your DataHolder
class?
is there anyway to mutate a
DataSnapshot$1
?
No, because it is an anonymous inner class that only has one method that returns a parameter. Parameters are passed by value, so you don't need to worry about callers changing their values on the other side. It's also a String
, which means the callers won't be mutating the object either.
You might be asking this because you saw how initialReader
has changed. Well, since DataHolder$1
is an inner class of the mutable DataHolder
, it's not really immutable even if it doesn't have any mutator methods.
is it safe to expose such a
DataHolder$1
, or is there anyway to mess with theDataHolder
instance from theDataHolder$1
object ?
There is no way to access the outer class from the inner class, so since there are no mutator methods in DataHolder$1
, you can't mutate DataHolder
from the outside, with only a DataHolder$1
.
However, if DataHolder
changes, the changes will reflect on DataHolder$1
(as shown in your sample code), which I think defeats the purpose of immutability.
Here's how I would implement immutability in this scenario.
I would make DataHolder
implement _Data
. DataHolder
certainly can do this, can't it? It has a String getData()
method after all! DataHolder
would have an asReadOnly
method that creates a copy of this
and returns _Data
. In fact, I'd rename _Data
to ReadOnlyData
.
How to make object immutable in java
- Make constructor private and provide createInstance method with the same attributes as constructor or factory method ? How does it helps ?
Answer: making the constructor private and providing createInstance()
(factory method) does not help by itself: it is one of few things you should do in order to allow users to actually use the class and its instances while you still have the control of the way instances are created.
- Make attributes final - the post fails to explain this point and somewhere I read to avoid the modification accidentally. How can you modify accidentally, when there are no mutators and class is final ? How making an attribute final is helping ?
Answer: declaring a class as final
means that the user can't extend it, so it "blocks" the user from this kind of "workaround". Declaring an attribute as final
won't allow the user of the class to change it. It cannot be "modified accidentally", but it can be "modified viciously" using reflection. Let's see an example, say you have:
final public class SomeClass {
final Integer i = 1;
}
from another class you can do as follows:
class AnotherClass {
public static void main (String[] args) throws Exception {
SomeClass p = new SomeClass();
Field i =p.getClass().getDeclaredField("i");
i.setAccessible(true);
i.set(p, 5);
System.out.println("p.i = " + p.i); // prints 5
}
}
- Can instead of factory use builder pattern ?
Answer: you can use the builder pattern or any pattern that helps you control the creation of instances of the class.
Further:
If you want to make sure your class is immutable, make sure that any getter
returns a deep-copy of the class member. This technique is called "protective/defensive copy". You can read more about it here
Any nice way to make a chain of immutable objects loop around?
One possible solution:
class HSequenceBuilder {
private long id = 0;
private final Map<Integer, H> map = new HashMap<Integer, H>();
static H create(int next) {
H h = new H(id, next, Collections.unmodifiableMap(map));
map.put(id, h);
id++;
return h;
}
static H create() {
create(id + 1);
}
}
class H {
private final id;
private final Map<Integer, H> map;
private final int next;
H(int id, int next, Map<Integer, H> map) {
this.id = id;
this.next = next;
this.map = map;
}
int getId() { return id; }
}
HSequenceBuilder builer = new HSequenceBuilder();
H h1 = builder.create(); // 1.
H h2 = builder.create(); // 2.
H h3 = builder.create(h2.getId()); // 3.
Related Topics
What Is Recommended Way for Spawning Threads from a Servlet in Tomcat
Jre 1.7 - Java Version - Returns: Java/Lang/Noclassdeffounderror: Java/Lang/Object
Deep Clone Utility Recommendation
Create Java Console Inside a Gui Panel
How to Convert Java String to Date Object
How to Check If Two Words Are Anagrams
Noclassdeffounderror on Maven Dependency
Java: Detect Duplicates in Arraylist
What Is the Use of Marker Interfaces in Java
Difference Between @JSONignore and @JSONbackreference, @JSONmanagedreference
Displaying Currency in Indian Numbering Format
What Is the Significance of Url-Pattern in Web.Xml and How to Configure Servlet
Method Chaining + Inheritance Don't Play Well Together
How to Convert a String to a Date Using Simpledateformat
How to Handle Pop-Up in Selenium Webdriver Using Java