2 решения, одно функциональное и одно объектное (это тот же код), потокобезопасный, без "if" и заботящийся об обработке исключений с правильным распространением типа (здесь нет решения, позаботьтесь об этом).
Он довольно короткий. Лучшая поддержка ленивых полей, обрабатываемая средой выполнения, в конечном итоге сделает этот код устаревшим...
использование :
// object version : 2 instances (object and lambda)
final Lazy<Integer, RuntimeException> lazyObject = new LazyField<>(() -> 1);
// functional version : more efficient than object, 1 instance
// usage : wrap computed value using eval(arg), and set the lazy field with result
Lazy<Service, IOException> lazyFunc = lazyField(() -> this.lazyFunc = eval(new Service()));
// functional final version, as field is final this is less efficient than object :
// 2 instances one "if" and one sync (that could still be avoided...)
final Lazy<Integer, RuntimeException> finalFunc = lazyField(() -> eval(1));
// Here the checked exception type thrown in lambda can only be ServiceException
static Lazy<Integer, ServiceException> lazyTest = lazyField(() -> {throw new ServiceException();});
Сначала я определяю лямбду с исключением:
@FunctionalInterface
interface SupplierWithException<T, E extends Exception> {
T get() throws E;
}
Затем ленивый тип:
interface Lazy<T, E extends Exception> extends SupplierWithException<T, E> {}
Функциональная версия:
Он напрямую возвращает лямбду, которая в конечном итоге занимает меньше памяти, если не используется в последнем поле, как в примере выше.
static <T, E extends Exception> Lazy<T, E> lazyField(Lazy<Lazy<T, E>, E> value) {
Objects.requireNonNull(value);
Lazy<T, E>[] field = new Lazy[1];
return () -> {
synchronized(field) {
if(field[0] == null)
field[0] = value.get();
return field[0].get();
}
};
}
static <T, E extends Exception> Lazy<T, E> eval(T value) {
return () -> value;
}
Нельзя принудительно указать правильный обратный вызов значения, по крайней мере, он всегда возвращает один и тот же результат, но не может избежать «если» (как в случае с окончательным полем).
Версия объекта:
Полностью безопасен снаружи.
public final class LazyField<T, E extends Exception> implements Lazy<T, E> {
private Lazy<T, E> value;
public LazyField(SupplierWithException<T, E> supplier) {
value = lazyField(() -> new Lazy<T, E>() {
volatile Lazy<T, E> memBarrier;
@Override
public T get() throws E {
value = memBarrier = eval(supplier.get());
}
});
}
@Override
public T get() throws E {
return value.get();
}
}
чтение значения поля неупорядочено, но использование volatile поля memBarrier обеспечивает упорядоченность значений, записанных в этом поле. Начальная лямбда, установленная в этом поле, также возвращает инициализированное ленивое значение, если вызывается после того, как ленивое значение было фактически установлено.
наслаждаться
person
Charles Briquel
schedule
10.07.2019
Lazy
в Java? - person rodolfino   schedule 19.03.2015