Saturday, May 14, 2011

Lazily Instantiate a Final Field

Java only allows you to instantiate final fields in your constructor, like this:
public class Test{
  private final Connection conn;

  public Test(){
      conn = new Connection();
  }

  public Connection getConnection(){
      return conn;
  }
}
Now, let's say that this field is expensive to create and so you would like to instantiate it lazily. We would like to be able to do something like the following (which won't compile because the final field has not been initialised in the constructor):
//does not compile!
public class Test{
  private final Connection conn;

  public Test(){
  }

  public Connection getConnection(){
      if(conn == null)
          conn = new Connection();
      return conn;
  }
}
So, there is no way to lazily instantiate a final field. However, with a bit of work, you can do it using Memoisation (with Callables). Simply wrap your field in a final Memo as shown below:
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;
  }
}

public class Test {
  private final Memo<Connection> conn;

  public Test() {
    conn = new Memo<Connection>(new Callable<Connection>() {
      public Connection call() throws Exception {
        return new Connection();
      }
    });
  }

  public Connection getConnection() {
    return conn.get();
  }
}

No comments:

Post a Comment