2

I was doing some reading here for creating immutable objects, and I was wondering, I have the following class:

final public class AnonymousCilent {

    final private String anonymousCilentID;

    public AnonymousCilent(String anonymousCilentID) {
        this.anonymousCilentID = anonymousCilentID; 
    }

This is the way Oracle suggests to create immutable classes, however, in C# you can declare your setter private, in Java is it still immutable if I do this?

private String anonymousCilentID;

public AnonymousCilent(String anonymousCilentID) {
    this.setAnonymousCilentID(anonymousCilentID);
}

public String getAnonymousCilentID() {
    return anonymousCilentID;
}

private void setAnonymousCilentID(String anonymousCilentID) {
    this.anonymousCilentID = anonymousCilentID;
}

What is wrong with implementing a private setter, instead of making my private variable final, and using a constructor?

Bohemian
  • 412,405
  • 93
  • 575
  • 722

6 Answers6

3

The reason is that setting the method private only effects the scope of the method. If you want a truly immutable variable then you must set it final as well. This way your variable cannot be mutated.

You could also return a clone of your userID in the getAnonymousClientID method.

All objects are mutable by default in Java.

EDIT: So all you would do is return your userID in your get method like this:

public String getUserID() {
    return (String) userID.clone();
}
andrewdleach
  • 2,458
  • 2
  • 17
  • 25
  • So, the proper practice is using a private final variable, to ensure no one call really change it, correct? –  Jul 03 '15 at 17:08
  • Yes. Or just making sure the variable implements Cloneable and return a copy to the requesting code with an object.clone() invocation – andrewdleach Jul 03 '15 at 17:09
  • Since `anonymousCilentID` is a String, cloning would not be necessary. – f_puras Jul 03 '15 at 17:10
  • Using the id variable, could you show me how to implement the cloneable variable? –  Jul 03 '15 at 17:10
  • 1 more quick question, if it's private final, I don't need to use cloneable method and return a copy correct? –  Jul 03 '15 at 17:11
  • That's fine too. Final means any mutation is impossible. See edit above for cloneable example. – andrewdleach Jul 03 '15 at 17:12
  • @andrewdleach - Thanks for code, and explanation. Much appreciated. –  Jul 03 '15 at 17:13
  • Of course. If I answered your question with my answer please mark as resolved for other devs on the forums. – andrewdleach Jul 03 '15 at 17:13
1

Making the field final does two things:

  • Requires the value to be set through the constructor (or at least be resolved at construction time)
  • Prohibits the creation of a setter for the field

Provided that the datatype at the field isn't mutable (like an array), that should be sufficient to make the object immutable. An immutable class basically means that its state can't be changed after it's instantiated, and if there are any changes, it would produce a new instance of the object.

Makoto
  • 104,088
  • 27
  • 192
  • 230
0

A private setter is completely pointless. Any code that can call the private setter can also write to the variable directly. That means there is no guarantee, just because you made the setter private, that code doesn't modify the variable.

DJClayworth
  • 26,349
  • 9
  • 53
  • 79
0

What is the point of writing a setter method only to make it private? There is no point and its usage in the constructor prevents you from declaring the field final. Declaring the field final is useful as it shows your intentions to the reader never to modify that field, it will also cause a compile error if you ever try to modify that field again, and it may also allow additional optimizations.

Ed Griffin
  • 456
  • 5
  • 14
0

Use a final field.

Putting final on a field tells any developer reading your code that the class is immutable - which is good.

However, having a mutable field (private setter or not) suggests it's OK to change the value as long as the instance itself changes it. ie some other programmer may come along and add a mutating method ,like clear() below:

private String anonymousCilentID;

public AnonymousCilent(String anonymousCilentID) {
    this.setAnonymousCilentID(anonymousCilentID);
}

public String getAnonymousCilentID() {
    return anonymousCilentID;
}

private void setAnonymousCilentID(String anonymousCilentID) {
    this.anonymousCilentID = anonymousCilentID;
}

// This method seems reasonable, but it isn't OK    
public void clear() {
   anonymousCilentID = null;          //   <--- opps!
}

This is bad.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
0

This is because if we use only 'private' for a field, an inner class could still change the state of the object.For example:

public class Test{
  public static void main(String[] args) {
    C c = new C();
    C.Inner i = c.new Inner();
    System.out.println(c.getA());  // prints 2
  }

}
class C{
  private int a = 1;
  public class Inner{
    public Inner(){
      a = 2;
    }
  }
  public int getA(){
    return a;
  }
}
Hicham Moustaid
  • 773
  • 6
  • 13