Published 06 Sep, 2022

Java - How to create a variable that can be set only once but isn't final in Java

Category Java
Modified : Sep 29, 2022
95

I want a class that I can create instances of with one variable unset (the id), then initialise this variable later, and have it immutable after initialisation. Effectively, I'd like a final variable that I can initialise outside of the constructor.

Currently, I'm improvising this with a setter that throws an Exception as follows:

public class Example {

    private long id = 0;

    // Constructors and other variables and methods deleted for clarity

    public long getId() {
        return id;
    }

    public void setId(long id) throws Exception {
        if ( this.id == 0 ) {
            this.id = id;
        } else {
            throw new Exception("Can't change id once set");
        }
    }
}

Is this a good way of going about what I'm trying to do? I feel like I should be able to set something as immutable after it's initialised, or that there is a pattern I can use to make this more elegant.

Answers

There are 4 suggested solutions here and each one has been listed below with a detailed description. The following topics have been covered briefly such as Java, Reference, Final. These have been categorized in sections for a clear and precise explanation.

61

Let me suggest you a little bit more elegant decision. First variant (without throwing an exception):

public class Example {

    private Long id;

    // Constructors and other variables and methods deleted for clarity

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = this.id == null ? id : this.id;
    }

}

Second variant (with throwing an exception):

     public void setId(long id)  {
         this.id = this.id == null ? id : throw_();
     }

     public int throw_() {
         throw new RuntimeException("id is already set");
     }

50

try have an int checker like

private long id = 0;
static int checker = 0;

public void methodThatWillSetValueOfId(stuff){
    checker = checker + 1

    if (checker==1){
        id = 123456;
    } 
}

48

Google's Guava library (which I recommend very highly) comes with a class that solves this problem very well: SettableFuture. This provides the set-once semantics that you ask about, but also a lot more:

  1. The ability to communicate an exception instead (the setException method);
  2. The ability to cancel the event explicitly;
  3. The ability to register listeners that will be notified when the value is set, an exception is notified or the future is canceled (the ListenableFuture interface).
  4. The Future family of types in general used for synchronization between threads in multithreaded programs, so SettableFuture plays very nicely with these.

Java 8 also has its own version of this: CompletableFuture.


47

Marking a field private and not exposing a setter should be sufficient:

public class Example{ 

private long id=0;  

   public Example(long id)  
   {  
       this.id=id;
   }    

public long getId()  
{  
     return this.id;
}  

if this is insufficient and you want someone to be able to modify it X times you can do this:

public class Example  
{  
    ...  
    private final int MAX_CHANGES = 1;  
    private int changes = 0;    

     public void setId(long id) throws Exception {
        validateExample(); 
        changes++; 
        if ( this.id == 0 ) {
            this.id = id;
        } else {
            throw new Exception("Can't change id once set");
        }
    }

    private validateExample  
    {  
        if(MAX_CHANGES==change)  
        {  
             throw new IllegalStateException("Can no longer update this id");   
        }  
    }  
}  

This approach is akin to design by contract, wherein you validate the state of the object after a mutator (something that changes the state of the object) is invoked.