懒惰实例化最终字段

时间:2009-03-23 09:35:44

标签: java final

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

以下代码无法编译:

public class Test{
    private final Connection conn;

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

有替代方案吗?

5 个答案:

答案 0 :(得分:8)

没有。最终字段的要点是它在构造期间设置一次,之后永远不会改变。在您的情况下,编译器或VM如何知道有关conn的任何有用信息?怎么会知道只有那个属性应该能够设置它,而不是其他方法呢?

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

答案 1 :(得分: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;
    }
}

现在我们可以创建一个最终的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();
}

Source

答案 2 :(得分:1)

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

答案 3 :(得分: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;
    }
}

答案 4 :(得分: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