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

后端 未结 12 1575
感情败类
感情败类 2020-12-14 05:43

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 initial

12条回答
  •  一整个雨季
    2020-12-14 06:39

    Here are two ways; the first is basically the same as some others mentioned in other answers, but it is here to constrast with the seconds. So the first way, Once is to have a value that can be set only once by enforcing that in the setter. My implementation requires non-null values, but if you want to be able to set to null, then you would need to implement an 'isSet' boolean flag as suggested in other answers.

    The second way, Lazy, is to provide a function that lazily supplies the value once the first time the getter is called.

    import javax.annotation.Nonnull;
    
    public final class Once 
    {
        private T value;
    
        public set(final @Nonnull T value)
        {
            if(null != this.value) throw new IllegalStateException("Illegal attempt to set a Once value after it's value has already been set.");
            if(null == value) throw new IllegalArgumentException("Illegal attempt to pass null value to Once setter.");
            this.value = value;
        }
    
        public @Nonnull T get()
        {
            if(null == this.value) throw new IllegalStateException("Illegal attempt to access unitialized Once value.");
            return this.value;
        }
    }
    
    public final class Lazy
    {
        private Supplier supplier;
        private T value;
    
        /**
         * Construct a value that will be lazily intialized the
         * first time the getter is called.
         *
         * @param the function that supplies the value or null if the value
         *        will always be null.  If it is not null, it will be called
         *        at most one time.  
         */
        public Lazy(final Supplier supplier)
        {
            this.supplier = supplier;
        }
    
        /**
         * Get the value.  The first time this is called, if the 
         * supplier is not null, it will be called to supply the
         * value.  
         *
         * @returns the value (which may be null)
         */
        public T get()
        {
            if(null != this.supplier) 
            {
                this.value = this.supplier.get();
                this.supplier = null;   // clear the supplier so it is not called again
                                        // and can be garbage collected.
            }
            return this.value;
        }
    }
    

    So you might use these as follows;

    //
    // using Java 8 syntax, but this is not a hard requirement
    //
    final Once i = Once<>();
    i.set(100);
    i.get();    // returns 100
    // i.set(200) would throw an IllegalStateException
    
    final Lazy j = Lazy<>(() -> i);
    j.get();    // returns 100
    

提交回复
热议问题