是否可以懒惰地实例化最终字段?
以下代码无法编译:
public class Test{
private final Connection conn;
public Connection getConnection(){
if(conn==null){
conn = new Connection();
}
return conn;
}
}
有替代方案吗?
不,final 字段的意义在于它在构造过程中设置一次,之后将永远不会更改。编译器或虚拟机如何知道对conn
您的情况有用的任何信息?它怎么知道只有那个属性才能设置它,而不是其他方法?
也许如果你解释了你想要的语义是什么,我们可以想出一个替代方案。您可能有一个“提供者”接口,代表一种获取值的方式,然后是MemoizingProvider
另一个提供者的代理,但只有一次,否则缓存该值。那也无法为缓存值提供最终字段,但至少它只会在一个地方。
这是使用 Memoisation(带有 Callables)的一种方法:
课堂备忘录:
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;
}
}
现在我们可以创建一个最终的连接!
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();
}
dhiller 的回答是经典的双重检查锁定错误,不要使用。
正如 Jon Skeet 所说,不,没有。
解释您的代码示例,您可能想要执行以下操作:
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;
}
}
作为旁注,可以更改最终字段。至少实例字段。你只需要一些反思:
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