Лениво создать экземпляр последнего поля - PullRequest
2 голосов
/ 23 марта 2009

Можно ли лениво создавать финальное поле?

Следующий код не компилируется:

public class Test{
    private final Connection conn;

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

Есть ли альтернатива?

Ответы [ 5 ]

8 голосов
/ 23 марта 2009

Нет. Смысл последнего поля в том, что оно устанавливается один раз во время строительства и никогда не изменится после этого. Как мог компилятор или ВМ узнать что-нибудь полезное о conn в вашем случае? Откуда ему знать, что только это свойство должно быть в состоянии установить его, а не какой-то другой метод?

Возможно, если бы вы объяснили, какой должна быть семантика, мы могли бы придумать альтернативу. Вы могли бы потенциально иметь интерфейс «провайдера», представляющий способ получения значения, а затем MemoizingProvider, который проксирует другого провайдера, но только один раз, кэшируя значение в противном случае. Это также не может иметь окончательного поля для кэшированного значения, но по крайней мере оно будет только в одном месте.

3 голосов
/ 23 марта 2009

Вот один из способов сделать это, используя 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();
}

Источник

1 голос
/ 23 марта 2009

Ответ Диллера - это классическая ошибка блокировки с двойной проверкой, не использовать.

0 голосов
/ 23 марта 2009

В качестве примечания можно изменить конечное поле. По крайней мере, экземпляры полей. Вам просто нужно немного размышлений:

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
0 голосов
/ 23 марта 2009

Как сказал Джон Скит, нет, нет.

Интерпретируя ваш пример кода, вы можете сделать что-то вроде этого:

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...