Reputation: 274898
Is it possible to lazily instantiate a final field?
The following code does not compile:
public class Test{
private final Connection conn;
public Connection getConnection(){
if(conn==null){
conn = new Connection();
}
return conn;
}
}
Is there an alternative?
Upvotes: 2
Views: 699
Reputation: 274898
Here's one way you can do it using Memoisation (with Callables):
Class Memo:
public class Memo<T> {
private T result;
private final Callable<T> callable;
private boolean established;
public Memo(final Callable<T> callable) {
this.callable = callable;
}
public T get() {
if (!established) {
try {
result = callable.call();
established = true;
}
catch (Exception e) {
throw new RuntimeException("Failed to get value of memo", e);
}
}
return result;
}
}
Now we can create a final conn!
private final Memo<Connection> conn = new Memo<Connection>(
new Callable<Connection>() {
public Connection call() throws Exception {
return new Connection();
}
});
public Connection getConnection() {
return conn.get();
}
Upvotes: 3
Reputation: 1377
dhiller's answer is the classic double checked locking bug, do not use.
Upvotes: 1
Reputation: 4777
As a side note, it's possible to change a final field. At least instance fields. You just need some reflection:
import java.lang.reflect.Field;
public class LazyFinalField {
private final String finalField = null;
public static void main(String[] args) throws Exception {
LazyFinalField o = new LazyFinalField();
System.out.println("Original Value = " + o.finalField);
Field finalField = LazyFinalField.class.getDeclaredField("finalField");
finalField.setAccessible(true);
finalField.set(o, "Hello World");
System.out.println("New Value = " + o.finalField);
}
}
Original Value = null
New Value = Hello World
Upvotes: 0
Reputation: 3505
As Jon Skeet said, no, there isn't.
Interpreting your code sample you may want to do something like this:
public class Test{
private final Object mutex = new Object(); // No public locking
private Connection conn;
public Connection getConnection(){
if(conn==null){
synchronized (mutex) {
if(conn==null){
conn = new Connection();
}
}
}
return conn;
}
}
Upvotes: 0
Reputation: 1504182
No. The point of a final field is that it's set once, during construction, and will never change thereafter. How could the compiler or the VM know anything useful about conn
in your case? How would it know that only that property should be able to set it, and not some other method?
Perhaps if you explained what you want the semantics to be, we could come up with an alterative. You could potentially have a "provider" interface representing a way to fetch a value, and then a MemoizingProvider
which proxies to another provider, but only once, caching the value otherwise. That wouldn't be able to have a final field for the cached value either, but at least it would only be in one place.
Upvotes: 8