0

Here is an example of an immutable class:

package com.immutable;

public final class ImmutableClass {

    private final int index;
    private final String tStr;
    private final ComplexObj cObj;

    public ImmutableClass(int i, String s, ComplexObj o){
        this.index = i;
        this.tStr  = s;

        ComplexObj cobj = new ComplexObj(o.someVar);
        this.cObj       = cobj;

    }

    public static void main(String[] args) {

        ImmutableClass icls = new ImmutableClass(5,"Hello World",new ComplexObj(100));

        System.out.println(icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);

        icls.cObj.someVar = 5;

        System.out.println("Second run :" + icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);

    }
}

And here is the implementation of the ComplexObj class:

package com.immutable;

public class ComplexObj {

    int  someVar;

    public ComplexObj(int i){
        this.someVar = i;
    }
}

When I create an instance of ImmutableClass I am making a deep copy of ComplexObj in the constructor of ImmutableClass, however I was able to update the value of cObj via icls.cObj.someVar that kind of breaks immutability of the my class. What am I doing wrong here?

akuzminykh
  • 4,522
  • 4
  • 15
  • 36
vs777
  • 564
  • 2
  • 11
  • 24
  • 1
    Your immutable class/object contains a mutable object, which as you see, can be changed. Don't hold references to mutable objects in your immutable class. – Progman Oct 26 '20 at 22:39
  • Use getter and setter for your fields, that way you can extend your classes and provide immutable solutions (like let the setter fail on access). – Progman Oct 26 '20 at 22:40
  • 5
    Does this answer your question? [How does the "final" keyword in Java work? (I can still modify an object.)](https://stackoverflow.com/questions/15655012/how-does-the-final-keyword-in-java-work-i-can-still-modify-an-object) – Charlie Armstrong Oct 26 '20 at 22:41
  • 5
    What you've demonstrated is that the class you've named `ImmutableClass` is not actually immutable, and to make it immutable, you can't give public access to mutable fields. – Louis Wasserman Oct 26 '20 at 22:55

3 Answers3

1

Your immutable class is like a titanium and concrete monument. Once created it is pretty much inpervious to vandalism.

Written on your monument is the location on the beach of a very pretty sand castle.

One person expects their full enjoyment of your monument to include finding that, driving over there, and gazing at the sand castle.

A second person drives over there and flattens the castle out.

The first person now feels their experience was changed.

A third person decides to hold a philosophical debate on what immutable means, and says that the monument has not changed at all: That location of the sand castle is still there, unchanged.

The first and third person decide to have a fist fight about it.

You tell me, who is right? The first, or the third?

Because it is an exact match to what's happening in your java code. You're like the first guy. Whomever said that 'make a class final, and every field final, and then the objects of that class will be immutable' is like the third.

If you want the monument's experience to not change, then either that sand castle needs to also be a titanium-and-concrete concept, which is not something the builders of this monument can do (you'd have to ask the builders of the sand castle to do this), or you need to not put the locations of non-impervious things on that monument.

In other words, either don't include fields of non-immutable types in your class if you want it to be 'experience' immutable - i.e. don't have fields of type ComplexObj, or alternatively, make those immutable too, i.e. edit ComplexObj.java and e.g. make that field final.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • I really enjoyed reading your metaphorical musings and I do agree with your point of view. Thank you. – vs777 Oct 27 '20 at 01:53
0

'immutable' only pertains to the fields associated with the class in question. It says that the fields defined in the class can't change.

In this case, one of the fields in an ImmutableClass instance is a reference to another object, a ComplexObj instance. All immutability of ImmutableClass says is that that field can't be changed after the object is created. In other words, you can't change an instance of ImmutableClass to point to a different ComplexObj object than the one it was initially pointing to.

None of this, however, has anything to do with changing the ComplexObj that the field within the ImmutableClass instance was pointing to. If the reference to that class is made available to an outside caller, then the caller can take that reference, and modify that object if it normally allows itself to be modified. ImmutableClass being immutable says nothing about if objects its fields are pointing to can change.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44
  • That's all true, but I am trying to understand whether it's possible at all to make a class immutable even when it contains a variable that's a reference to the different class. In my case specifically I made a deep copy of ComplexObj rather than using an object itself – vs777 Oct 27 '20 at 01:48
  • The ImmutableClass class IS immutable. But I guess I know what you mean. Why don't you just not give anyone access to the ComplexObj class. You could wrap ComplexObj in another class like ReadOnlyComplexObj that delegates all of its operations to a copy of ComplexObj that only the wrapper has access to. This would be a killer way to go if it turns out that ComplexObj is implementing an interface, and you're only calling the methods in that interface. Then you could have ReadOnlyComplexObj just implement the same interface, and only pass through the operations that don't change the object. – CryptoFool Oct 27 '20 at 02:18
  • yep, makes sense. thank you. – vs777 Oct 27 '20 at 03:42
0

Your class is fine and it's truly immutable as long the reference of the mutable object behind cObj doesn't escape the scope of a wrapping instance of ImmutableClass.

You are able to access cObj and change a field of it via icls.cObj.someVar because you do it within main, which is a class method of ImmutableClass. Class methods (methods marked with static) of a class C are able to access C's private class and object fields.

Move the class ImmutableClass in its own file and you'll notice that icls.cObj.someVar will give you an error at compile time. Or extract main out of ImmutableClass any other way you like. E.g.:

public class ImmutableClass {

    private final int index;
    private final String tStr;
    private final ComplexObj cObj;

    public ImmutableClass(int i, String s, ComplexObj o){
        this.index = i;
        this.tStr  = s;
        ComplexObj cobj = new ComplexObj(o.someVar);
        this.cObj       = cobj;
    }
}
class Main {

    public static void main(String[] args) {
        ImmutableClass icls = new ImmutableClass(5,"Hello World",new ComplexObj(100));
        System.out.println(icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);
        icls.cObj.someVar = 5;
        System.out.println("Second run :" + icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);
    }
}
akuzminykh
  • 4,522
  • 4
  • 15
  • 36
  • I created an extra class and replicated "main" method of ImmutableClass. Still was able to change "icls.cObj.someVar" – vs777 Oct 27 '20 at 01:39
  • @vs777 That's because you didn't read my answer correctly and did something else. See the edit for an example how it was meant to be done. – akuzminykh Oct 27 '20 at 12:34