简体   繁体   中英

making a class immutable in java

To make a class immutable what I can do is:

1)Make class final
2)do not provide setters
3)mark all variables as final

But if my class has another object of some other class then , somone can change value of that object

class MyClass{
 final int a;
 final OtherClass other 

 MyClass(int a ,OtherClass other){
  this.a = a;
  this.other = other;
 }

 int getA(){
   return a;
 }

 OtherClass getOther(){
   return other;
 }

 public static void main(String ags[]){
  MyClass m = new Myclass(1,new OtherClass);
  Other o = m.getOther();
  o.setSomething(xyz) ; //This is the problem ,How to prevent this?

}
}

A) Make the OtherClass immutable as well

or

B) Don't allow direct access to the OtherClass object, instead providing only getters to act as a proxy.

Edit to add: You could make a deep copy of OtherClass and return a copy rather than the original, but that generally isn't the type of behavior you would expect in Java.

Immutability is best considered from the perspective of the API user. So your object API needs to satisfy the following two conditions:

  1. No way for an external user to change the value of the object
  2. A guarantee that any time the user reads or makes use of the object's value in the future, it will get the same result

Important note : It is in fact OK to have mutable data inside an immutable object as long as it behaves as an immutable object from the perspective of the API user . Consider java.lang.String for example: although it is generally considered as the definitive immutable class, it does in fact have a mutable internal field for caching the hashCode (not many people know this!).

So to address your question, if you wish to contain another (mutable) object inside an immutable object then you typically need to do one or more of the following:

  • Guarantee that nobody else can change the value of the mutable object. Typically this means ensuring that no-one else can have a reference to the mutable object, so this is only usually possible if you create the object yourself rather than accept a reference from outside.
  • Take a defensive deep copy of the mutable object, and don't hand out references to the new copy. Only allow operations that read the new copy in the public API. If you need to hand out a reference to this object, then you need to take another defensive copy (to avoid handing out a reference to the internal copy).
  • Use an immutable wrapper for the mutable object. Something like Collections.unmodifiableList . This is useful if you want to hand out a reference to the internal mutable object but don't want to run the risk of it being modified.

All of these solutions are a bit hacky - a better solution overall is to avoid the use of mutable objects within immutable objects. In the long run it's asking for trouble because sooner or later a mutable reference will leak out and you will have an extremely hard to find bug. You are better moving towards a full hierarchy of immutable objects (the approach taken by languages like Scala and Clojure)

It is possible to create the immutable class in java by following ways

1.Don't Provide setter methods.

2.Make all fields are as final and private.

3.Make Class as final.

I assume OtherClass (by the way you say Other once) is meant to be a class you don't control, or which has to have a setter.

If you can't remove getOther , change it to getOtherView and return a read-only view of other . There will be wrappers for all the get methods, but no set ones.

Return deep clones from your getters. You may find this to be no easy task.

All the objects referenced in the immutable class should be immutable, or at least be encapsulated as private and making sure that they are not modified (not inside the methods of your class and definitely not from the outside). For instance, if you have this situation:

public class MyImmutable {
    private MutableClass mutableObject;
}

... You can not provide the getMutableObject() method, because doing so will open the door for outside modifications, like this:

myImmutable.getMutableObject().setSomeAttribute(newValue);

As a special case of the above, all collections and/or maps should be made immutable, with the ummodifiableXXX() methods in the Collections class.

you cannot (reasonably) stop that in java. if you don't have control over the other class, there are ways to effectively get immutable behavior, but it can be very expensive in practice. basically, you must always return a copy of that class in any public method return values. (the jdk actually has this problem with the TimeZone class).

But If my class has another object of some other class then , somone can change value of that object...

Java objects are not primitive. If you mark a primitive as final, then its value cannot be changed once it is assigned. However, object contents cannot be final, only object references can be final. So you cannot make an object in this way.

One solution might be abandoning all setter/mutator methods those could change the particular fields of the object and encapsulating them in a way that you can only access them, not change them.

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