Отложенная инициализация неизменяемых переменных - PullRequest
4 голосов
/ 09 ноября 2010

Я часто использую идиому scala lazy val, и я хотел бы добиться чего-то подобного в Java.Моя основная проблема заключается в том, что для создания некоторого значения мне нужно другое значение, которое не известно во время создания объекта, но я не хочу иметь возможность изменить его впоследствии.Причина в том, что я использую библиотеку GUI, которая создает объект от моего имени и вызывает другой метод, когда все, что мне нужно, создано, когда я знаю значения, которые мне нужны.

Вотсвойства, которые я пытаюсь достичь:
* Неизменяемость моей переменной.
* Инициализация в другом методе, отличном от конструктора.

Я не думаю, что это возможно в Java, только для finalдостигается неизменность переменной, и final переменные не могут быть инициализированы вне конструктора.

Что было бы в Java ближе всего к тому, чего я пытаюсь достичь?

Ответы [ 2 ]

5 голосов
/ 09 ноября 2010

Один из способов сделать это состоит в том, чтобы вставить фактическую реализацию рассматриваемого значения в другой класс. Это будет окончательным, но на самом деле не будет создано до тех пор, пока класс не будет загружен, который откладывается до тех пор, пока он не понадобится. Примерно так:

public class MyClass
{
    private static class Loader
    {
        public static final INSTANCE = new Foo();
    }

    Foo getInstance()
    {
        return Loader.INSTANCE;
    }
}

Это будет лениво инициализировать Foo, как и при желании.

Если вам абсолютно необходимо, чтобы Foo была переменной экземпляра вашего класса верхнего уровня - я не могу придумать никакого способа сделать это. Как вы заметили, переменная должна быть заполнена в конструкторе .

На самом деле я не совсем уверен, как Scala справляется с этим, но я предполагаю, что он устанавливает переменную lazy val в некоторый вид thunk , который заменяется реальным объектом при первом оценены. Scala, конечно, может сделать это, подавив обычные модификаторы доступа в этом случае, но я не думаю, что вы можете прозрачно сделать это в Java. Вы можете объявить поле, например, Future<Foo>, который создает значение при первом вызове и кэширует его с этого момента, но он не является прозрачным по ссылкам, и по определению final я не вижу способа обойти это.

1 голос
/ 09 ноября 2010

Ответ Анджея великолепен, но есть и способ сделать это без изменения исходного кода.Используйте AspectJ для захвата вызовов конструктора и возврата неинициализированных объектов:

pointcut lazyInit() : execution(* com.mycompany.expensiveservices.*.init(*));

void around()  : lazyInit() && within(@Slow *) {

    new Thread(new Runnable(){

        @Override
        public void run(){
            // initialize Object in separate thread
            proceed();
        }
    }
}

С учетом этого аспекта все конструкторы объектов, отмеченных аннотациями @Slow, будут выполняться в отдельном потоке.

Я не нашел много ссылок на ссылки, но, пожалуйста, прочитайте AspectJ в действии Рамнивас Ладдад для получения дополнительной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...