Declare final variable, but set later

Solution 1:

You can set a final variable only in a constructor or in an initializer. Regular methods cannot change the value of variables declared final.

Solution 2:

You can't. But you can guarantee no external object changes it if it's private and you don't have a setter for it.


Alternatively, you can wrap the long value in another class - LazyImmutableLong. But this is a more verbose approach, and you probably don't need it (note: the class below is not thread-safe)

class LazyImmutableLong {

    private Long value;

    public void setValue(long value) {
         if (this.value != null) {
             return; // the value has already been set
         }
         this.value = value;
    }
    public long getValue() {return value;}
}

And in your activity

private LazyImmutableLong id = new LazyImmutableLong();

public void onCreate(..) {
    id.setValue(..);
}

Solution 3:

The following Worm (Write-Once-Read-Many) class could help in this kind of scenario.

We could create a nested Wrapper class, that stores the final variable you need. To initialize this variable, you just should call a constructor of the wrapper object. When you call the method getData(), you will get a reference of the final variable in case it is initialized, otherwise, you will get null.

The methods getData() and setData(T data) are required to be thread-safe. To provide it, we use a volatile modifier for the wrapper object. Reading a volatile variable is synchronized and writing to a volatile variable is synchronized, too. Even though some efforts were made to make this code thread-safe I didn't test it in this respect. Depending on the level of thread safety you may consider to make setter and getter synchronized.

public class Worm<T> {
    private volatile Wrapper<T> wrapper;

    public Worm() {}
    public Worm(T data) throws IllegalAccessError
    {
        setData(data);
    }

    public T getData()
    {
        if (wrapper == null)
            return null;
        return wrapper.data;
    }

    public void setData(T data) throws IllegalAccessError
    {
        if (wrapper != null)
            throw new IllegalAccessError();
        else
            wrapper = this.new Wrapper<>(data);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Worm<T> other = (Worm<T>) obj;
        return Objects.equals(this.getData(), other.getData());
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(this.getData());
    }

    final private class Wrapper<T> {
        final private T data;

        Wrapper(T data) {
            this.data = data;
        }
    }
}