Я пытаюсь написать универсальную функциональность, которая по требованию создает поточную инициализацию по требованию. Я не могу использовать стандартный шаблон, так как значение не является окончательным и могло быть установлено через сеттер уже.Получите или создайте шаблон в Java 7?
В Java 8 я решил, что, написав общий LazyInitializer
с поставщиком:
public class LazyInitializer<T> {
protected final Supplier<T> initializer;
protected AtomicReference<T> value = new AtomicReference<>();
public LazyInitializer(final Supplier<T> initializer) {
this.initializer = initializer;
}
public T get() {
T result = this.value.get();
if (result == null) {
this.value.compareAndSet(null, this.initializer.get());
result = this.value.get();
}
return result;
}
public void setValue(final T value) {
this.value.set(value);
}
}
Вы бы затем использовать этот класс, как это:
final LazyInitializer<List<String>> value = new LazyInitializer<>(ArrayList<String>::new);
Это потокобезопасно, может обрабатывать сеттеры, имеет очень низкие накладные расходы и особенно: требуется небольшой код шаблона.
Теперь, однако, я вынужден использовать Java 7, и я не могу найти столь же элегантное решение, поскольку Java 7 не может использовать Поставщиков и поэтому требует написать много уродливого кода. Также дженерики не позволяют создавать их, если вы не дадите точный класс, что является проблемой, если вы используете общие значения классов (например, ArrayList<String>
).
Насколько я могу судить, я либо вынужден писать уродливый код, либо делать магию отражения за пределами моих возможностей, или есть способ написать класс LazyInitializer
элегантным способом в Java 7, который мне не хватает?
Edit: Использование ответа от Йорн Vernee я изменил класс для работы с Java 7 следующим образом:
public class LazyInitializer<T> {
protected final Class<?> clazz;
protected AtomicReference<T> value = new AtomicReference<>();
public LazyInitializer(final Class<?> clazz) {
this.clazz = clazz;
}
public T get() {
T result = this.value.get();
if (result == null) {
this.value.compareAndSet(null, constructNew());
result = this.value.get();
}
return result;
}
public void setValue(final T value) {
this.value.set(value);
}
protected T constructNew() {
try {
return (T) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
}
}
, который затем может быть (еще раз) элегантно называется так:
final LazyInitializer<List<String>> value = new LazyInitializer<>(ArrayList.class);
Однако этот класс больше не может проверить, действительно ли поставляемый класс соответствует (поскольку Generics), и он работает только с конструкторами по умолчанию. Но по крайней мере это решает мое дело.
Я ошибаюсь или вы можете пропустить первый 'this.value.get()' и if-предложение в вашем примере кода Java8? – Alexander
В случае, когда 'значение == null', значение должно быть установлено на начальное значение. Так что нет, вы не можете пропустить это. – TwoThe
@TwoThe Я ошибаюсь, или ваш код может фактически инициировать выполнение поставщика более одного раза? Поскольку оценка параметра для 'compareAndSet' не является атомарной с самой функцией. – chimmi