0

Consider following code

final class immudemo
{
      private static final StringBuffer bf = new StringBuffer("Yaxita");
      public StringBuffer getter()
      {
           return bf;
      }
}

public class HelloWorld{

     public static void main (String args[])
     {
         immudemo obj1 = new immudemo();
         StringBuffer bf2 = obj1.getter();
         bf2.append("Shah");
         System.out.println(obj1);
     }
}

in above code even though StringBuffer declared as final i am able to change it. Can anyone please help me how to achieve 100% immutability ?

P.S. : I want to achieve this with StringBuffer only. If you provide anything please check it should be related to StringBuffer only.

Yaxita Shah
  • 1,206
  • 1
  • 11
  • 17
  • 2
    Erm, use String and not Stringbuffer? I mean thats the point of Stringbuffer, that it is mutable in contrast to normal Strings. Edit: Apart from that, there is no operation to make a mutable class immutable. If you want a class to be immutable it has to be programmed that way. But i still don't get what the point of an immutable StringBuffer would be. – OH GOD SPIDERS Feb 23 '17 at 10:28
  • that's right .. but i have been asked to do it with StringBuffer only. Questioner wanted to know how can i create user defined class which is 100% immutable – Yaxita Shah Feb 23 '17 at 10:31
  • 1
    @YaxitaShah as you mentioned in your comment "Questioner wanted to know how can i create user defined class which is 100% immutable". He wanted to know how you would create your own class with same functionality same as of String class and how you achieve immutability in it. – Chetan Hallan Feb 23 '17 at 10:38
  • @ChetanHallan .. Yes right .. String is 100% immutable and he wanted me to create some class same as string class .. how can we create that ? Can you give me one example? – Yaxita Shah Feb 23 '17 at 10:42
  • 1
    Possible duplicate of [Immutable class?](http://stackoverflow.com/questions/3162665/immutable-class) – OH GOD SPIDERS Feb 23 '17 at 10:44

4 Answers4

2

With final you define, that you cannot set another object to "bf". The status of the object itself still can be changed if it is mutable. That is the case for StringBuffer. And because you release it to the outside by "getter", your class "immudemo" is also mutable.

If you use String as return type for "getter", you would have an immutable object on the outside, but a mutable on the inside. (Your class "immudemo" does not need to be final for immutability of its instances.)

Therefore your class "immudemo" would be immutable, if you return the type String instead of StringBuffer by your method "getter".

class immudemo
{
      private static final StringBuffer bf = new StringBuffer("Yaxita");
      public String getter(){
           return bf.toString();
      }
}

public class HelloWorld{

     public static void main (String args[])
     {
         immudemo obj1 = new immudemo();
         String bf2 = obj1.getter();
         bf2.append("Shah"); // does not work anymore
         System.out.println(obj1);
     }
}

Additional: Follow this strategy to define immutable objects yourself: https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html

cybi
  • 99
  • 5
  • There isnt anything about stringbuffer in this link – Yaxita Shah Feb 23 '17 at 10:43
  • No, there isn't, because StringBuffer is not immutable. It is a general strategy. Have a look at my answer again. I have added a possible fix at the bottom. – cybi Feb 23 '17 at 10:49
  • 1
    But what do you mean with "StringBuffer only"? If you have an mutable object inside another object and the outer object publishes the mutable object by a method, you cannot achieve 100% immutability. To achieve 100% immutability you need to return an immutable object, even though your internal objects are mutable. Therefore the easiest fix would be to return String by "getter". – cybi Feb 23 '17 at 11:02
2

YaxitaShah , in this case , StringBuffer declared as final , but StringBuffer references are passed through the getter method in the immudemo class;

may be you can:

final class immudemo
{
      private static final StringBuffer bf = new StringBuffer("Yaxita");
      public StringBuffer getter(){
           return new StringBuffer(bf);
      }
      
      public String toString() {
       return bf.toString();
      }
}

public class HelloWorld{

     public static void main (String args[])
     {
         immudemo obj1 = new immudemo();
         StringBuffer bf2 = obj1.getter();
         bf2.append("Shah");
         System.out.println(obj1);
     }
}
James Ven
  • 57
  • 2
2

in above code even though StringBuffer declared as final i am able to change it.

No, append() is not creating new object of your Stringbuffer, it just modifying the value of the same referenced object.

See the java doc of final variable which says,

Once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.

So, Immutable class can be by,

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

To make the StringBuilder class as Immutable check the below class, testSB instance variable, define the return type of the getter method as StringBuilder but maintain internally as String objects...

public class ImmutableClass {

    private final int value;
    private final String name;
    private final StringBuilder name1;
    private final String testSB;

    // changed the constructor, to say Immutable, instead of mutable
    public ImmutableClass( int aValue,  String aName,  StringBuilder aName1,StringBuilder aTestSB) {
        // The value is set. Now, and forever.
        value = aValue;
        name1=aName1;
        name=aName;
        testSB=new String(aTestSB);
    }

    public StringBuilder getTestSB() {
        return new StringBuilder(testSB);
    }

    public String getName() {
        return name;
    }

    public StringBuilder getName1() {
        return name1;
    }

    public final int getValue() {
        return value;
    }

}