2

是否可以懒惰地实例化最终字段?

以下代码无法编译:

public class Test{
    private final Connection conn;

    public Connection getConnection(){
        if(conn==null){
            conn = new Connection();
        }
        return conn;
    }
}

有替代方案吗?

4

5 回答 5

8

不,final 字段的意义在于它在构造过程中设置一次,之后将永远不会更改。编译器或虚拟机如何知道对conn您的情况有用的任何信息?它怎么知道只有那个属性才能设置它,而不是其他方法?

也许如果你解释了你想要的语义是什么,我们可以想出一个替代方案。您可能有一个“提供者”接口,代表一种获取值的方式,然后是MemoizingProvider另一个提供者的代理,但只有一次,否则缓存该值。那也无法为缓存值提供最终字段,但至少它只会在一个地方。

于 2009-03-23T09:38:47.673 回答
3

这是使用 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();
}

来源

于 2009-03-23T16:01:45.867 回答
1

dhiller 的回答是经典的双重检查锁定错误,不要使用。

于 2009-03-23T14:45:48.153 回答
0

正如 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;
    }
}
于 2009-03-23T12:33:49.343 回答
0

作为旁注,可以更改最终字段。至少实例字段。你只需要一些反思:

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
于 2009-03-23T14:44:58.360 回答