diff --git a/src/main/java/net/minecraftforge/common/util/LazyOptional.java b/src/main/java/net/minecraftforge/common/util/LazyOptional.java index b4643a9e0f..a169864d66 100644 --- a/src/main/java/net/minecraftforge/common/util/LazyOptional.java +++ b/src/main/java/net/minecraftforge/common/util/LazyOptional.java @@ -5,6 +5,7 @@ package net.minecraftforge.common.util; +import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Objects; import java.util.Optional; @@ -40,15 +41,14 @@ import org.jetbrains.annotations.Nullable; */ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class LazyOptional -{ +public class LazyOptional { private final NonNullSupplier supplier; private final Object lock = new Object(); // null -> not resolved yet // non-null and contains non-null value -> resolved // non-null and contains null -> resolved, but supplier returned null (contract violation) private Mutable resolved; - private Set>> listeners = new HashSet<>(); + private Set>>> listeners = new HashSet<>(); private boolean isValid = true; private static final @NotNull LazyOptional EMPTY = new LazyOptional<>(null); @@ -62,16 +62,14 @@ public class LazyOptional * null, but can be null itself. If null, this method * returns {@link #empty()}. */ - public static LazyOptional of(final @Nullable NonNullSupplier instanceSupplier) - { + public static LazyOptional of(final @Nullable NonNullSupplier instanceSupplier) { return instanceSupplier == null ? empty() : new LazyOptional<>(instanceSupplier); } /** * @return The singleton empty instance */ - public static LazyOptional empty() - { + public static LazyOptional empty() { return EMPTY.cast(); } @@ -83,27 +81,21 @@ public class LazyOptional * @return This {@link LazyOptional}, cast to the inferred generic type */ @SuppressWarnings("unchecked") - public LazyOptional cast() - { + public LazyOptional cast() { return (LazyOptional)this; } - private LazyOptional(@Nullable NonNullSupplier instanceSupplier) - { + private LazyOptional(@Nullable NonNullSupplier instanceSupplier) { this.supplier = instanceSupplier; } - private @Nullable T getValue() - { + private @Nullable T getValue() { if (!isValid || supplier == null) return null; - if (resolved == null) - { - synchronized (lock) - { + if (resolved == null) { + synchronized (lock) { // resolved == null: Double checked locking to prevent two threads from resolving - if (resolved == null) - { + if (resolved == null) { T temp = supplier.get(); if (temp == null) LOGGER.catching(Level.WARN, new NullPointerException("Supplier should not return null value")); @@ -114,13 +106,10 @@ public class LazyOptional return resolved.getValue(); } - private T getValueUnsafe() - { + private T getValueUnsafe() { T ret = getValue(); if (ret == null) - { throw new IllegalStateException("LazyOptional is empty or otherwise returned null from getValue() unexpectedly"); - } return ret; } @@ -130,8 +119,7 @@ public class LazyOptional * @return {@code true} if this {@link LazyOptional} is non-empty, i.e. holds a * non-null supplier */ - public boolean isPresent() - { + public boolean isPresent() { return supplier != null && isValid; } @@ -142,8 +130,7 @@ public class LazyOptional * @param consumer The {@link NonNullConsumer} to run if this optional is non-empty. * @throws NullPointerException if {@code consumer} is null and this {@link LazyOptional} is non-empty */ - public void ifPresent(NonNullConsumer consumer) - { + public void ifPresent(NonNullConsumer consumer) { Objects.requireNonNull(consumer); T val = getValue(); if (isValid && val != null) @@ -169,8 +156,7 @@ public class LazyOptional * present, otherwise an empty {@link LazyOptional} * @throws NullPointerException if {@code mapper} is null. */ - public LazyOptional lazyMap(NonNullFunction mapper) - { + public LazyOptional lazyMap(NonNullFunction mapper) { Objects.requireNonNull(mapper); return isPresent() ? of(() -> mapper.apply(getValueUnsafe())) : empty(); } @@ -189,8 +175,7 @@ public class LazyOptional * present, otherwise an empty {@link Optional} * @throws NullPointerException if {@code mapper} is null. */ - public Optional map(NonNullFunction mapper) - { + public Optional map(NonNullFunction mapper) { Objects.requireNonNull(mapper); return isPresent() ? Optional.of(mapper.apply(getValueUnsafe())) : Optional.empty(); } @@ -211,8 +196,7 @@ public class LazyOptional * @throws NullPointerException If {@code predicate} is null and this * {@link Optional} is non-empty */ - public Optional filter(NonNullPredicate predicate) - { + public Optional filter(NonNullPredicate predicate) { Objects.requireNonNull(predicate); final T value = getValue(); // To keep the non-null contract we have to evaluate right now. Should we allow this function at all? return value != null && predicate.test(value) ? Optional.of(value) : Optional.empty(); @@ -222,8 +206,7 @@ public class LazyOptional * Resolves the value of this LazyOptional, turning it into a standard non-lazy {@link Optional} * @return The resolved optional. */ - public Optional resolve() - { + public Optional resolve() { return isPresent() ? Optional.of(getValueUnsafe()) : Optional.empty(); } @@ -234,8 +217,7 @@ public class LazyOptional * @param other the value to be returned if this {@link LazyOptional} is empty * @return the result of the supplier, if non-empty, otherwise {@code other} */ - public T orElse(T other) - { + public T orElse(T other) { T val = getValue(); return val != null ? val : other; } @@ -251,8 +233,7 @@ public class LazyOptional * @throws NullPointerException If {@code other} is null and this * {@link LazyOptional} is non-empty */ - public T orElseGet(NonNullSupplier other) - { + public T orElseGet(NonNullSupplier other) { T val = getValue(); return val != null ? val : other.get(); } @@ -273,8 +254,7 @@ public class LazyOptional * @throws NullPointerException If {@code exceptionSupplier} is null and this * {@link LazyOptional} is empty */ - public T orElseThrow(NonNullSupplier exceptionSupplier) throws X - { + public T orElseThrow(NonNullSupplier exceptionSupplier) throws X { T val = getValue(); if (val != null) return val; @@ -286,16 +266,19 @@ public class LazyOptional *

* If this {@link LazyOptional} is empty, the listener will be called immediately. */ - public void addListener(NonNullConsumer> listener) - { + public void addListener(NonNullConsumer> listener) { if (isPresent()) - { - this.listeners.add(listener); - } + this.listeners.add(new WeakReference<>(listener)); else - { listener.accept(this); - } + } + + /** + * Unregisters a {@link NonNullConsumer listener} from the list to be notified when this {@link LazyOptional} becomes invalid (via {@link #invalidate()}).
+ * This allows modder who know they will not need to be notified, to remove the hard reference that this holds to their listener. + */ + public void removeListener(NonNullConsumer> listener) { + this.listeners.removeIf(e -> e.get() == null || e.get() == listener); } /** @@ -312,12 +295,14 @@ public class LazyOptional * etc... This allows modders to keep a cache of capability objects instead of * re-checking them every tick. */ - public void invalidate() - { - if (this.isValid) - { + public void invalidate() { + if (this.isValid) { this.isValid = false; - this.listeners.forEach(e -> e.accept(this)); + this.listeners.stream() + .map(WeakReference::get) + .filter(e -> e != null) + .forEach(e -> e.accept(this)); + this.listeners.clear(); } } }