diff --git a/src/main/java/org/springframework/data/util/Lazy.java b/src/main/java/org/springframework/data/util/Lazy.java index 789f966f7..840c2d285 100644 --- a/src/main/java/org/springframework/data/util/Lazy.java +++ b/src/main/java/org/springframework/data/util/Lazy.java @@ -18,6 +18,7 @@ package org.springframework.data.util; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; +import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; @@ -37,7 +38,7 @@ import org.springframework.util.Assert; @EqualsAndHashCode public class Lazy implements Supplier { - private final Supplier supplier; + private final Supplier supplier; private @Nullable T value = null; private boolean resolved = false; @@ -48,10 +49,24 @@ public class Lazy implements Supplier { * @param supplier the {@link Supplier} to create the object lazily. * @return */ - public static Lazy of(Supplier supplier) { + public static Lazy of(Supplier supplier) { return new Lazy<>(supplier); } + /** + * Creates a new {@link Lazy} to return the given value. + * + * @param the type of the value to return eventually. + * @param value the value to return. + * @return + */ + public static Lazy of(T value) { + + Assert.notNull(value, "Value must not be null!"); + + return new Lazy<>(() -> value); + } + /** * Returns the value created by the configured {@link Supplier}. Will return the calculated instance for subsequent * lookups. @@ -69,6 +84,42 @@ public class Lazy implements Supplier { return value; } + /** + * Returns the {@link Optional} value created by the configured {@link Supplier}, allowing the absence of values in + * contrast to {@link #get()}. Will return the calculated instance for subsequent lookups. + * + * @return + */ + public Optional getOptional() { + return Optional.ofNullable(getNullable()); + } + + /** + * Returns a new Lazy that will consume the given supplier in case the current one does not yield in a result. + * + * @param supplier must not be {@literal null}. + * @return + */ + public Lazy or(Supplier supplier) { + + Assert.notNull(supplier, "Supplier must not be null!"); + + return Lazy.of(() -> orElseGet(supplier)); + } + + /** + * Returns a new Lazy that will return the given value in case the current one does not yield in a result. + * + * @param supplier must not be {@literal null}. + * @return + */ + public Lazy or(T value) { + + Assert.notNull(value, "Value must not be null!"); + + return Lazy.of(() -> orElse(value)); + } + /** * Returns the value of the lazy computation or the given default value in case the computation yields * {@literal null}. @@ -78,7 +129,10 @@ public class Lazy implements Supplier { */ @Nullable public T orElse(@Nullable T value) { - return orElseGet(() -> value); + + T nullable = getNullable(); + + return nullable == null ? value : nullable; } /** @@ -89,13 +143,13 @@ public class Lazy implements Supplier { * @return */ @Nullable - public T orElseGet(Supplier supplier) { + private T orElseGet(Supplier supplier) { Assert.notNull(supplier, "Default value supplier must not be null!"); - T nullable = getNullable(); + T value = getNullable(); - return nullable == null ? supplier.get() : nullable; + return value == null ? supplier.get() : value; } /** @@ -104,7 +158,7 @@ public class Lazy implements Supplier { * @param function must not be {@literal null}. * @return */ - public Lazy map(Function function) { + public Lazy map(Function function) { Assert.notNull(function, "Function must not be null!"); @@ -117,7 +171,7 @@ public class Lazy implements Supplier { * @param function must not be {@literal null}. * @return */ - public Lazy flatMap(Function> function) { + public Lazy flatMap(Function> function) { Assert.notNull(function, "Function must not be null!"); diff --git a/src/test/java/org/springframework/data/util/LazyUnitTests.java b/src/test/java/org/springframework/data/util/LazyUnitTests.java index c7189b015..83aeb4f2b 100755 --- a/src/test/java/org/springframework/data/util/LazyUnitTests.java +++ b/src/test/java/org/springframework/data/util/LazyUnitTests.java @@ -27,6 +27,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** + * Unit tests for {@link Lazy}. + * * @author Oliver Gierke */ @RunWith(MockitoJUnitRunner.class) @@ -35,19 +37,6 @@ public class LazyUnitTests { @Mock Supplier supplier; @Mock Supplier> optionalSupplier; - @Test - public void invokesSupplierOnFirstAccess() { - - doReturn("foo").when(supplier).get(); - - Lazy lazy = Lazy.of(supplier); - - assertThat(lazy.get()).isEqualTo("foo"); - assertThat(lazy.get()).isEqualTo("foo"); - - verify(supplier, times(1)).get(); - } - @Test public void createsLazyOptional() { @@ -55,4 +44,70 @@ public class LazyUnitTests { assertThat(Lazy.of(optionalSupplier).get()).isEmpty(); } + + @Test + public void createsLazyFromValue() { + + Object value = new Object(); + + assertThat(Lazy.of(value).get()).isEqualTo(value); + } + + @Test + public void returnsLastValueInChain() { + + Object reference = new Object(); + + Object foo = Lazy.of(() -> null) // + .or(() -> null) // + .or(() -> reference) // + .get(); + + assertThat(foo).isEqualTo(reference); + } + + @Test + public void returnsCachedInstanceOnMultipleAccesses() { + + Lazy lazy = Lazy.of(() -> new Object()); + + assertThat(lazy.get()).isSameAs(lazy.get()); + } + + @Test + public void rejectsNullValueLookup() { + + assertThatExceptionOfType(IllegalStateException.class) // + .isThrownBy(() -> Lazy.of(() -> null).get()); + } + + @Test + public void allowsNullableValueLookupViaOptional() { + assertThat(Lazy.of(() -> null).getOptional()).isEmpty(); + } + + @Test + public void ignoresElseIfValuePresent() { + + Object first = new Object(); + Object second = new Object(); + + Lazy nonEmpty = Lazy.of(() -> first); + + assertThat(nonEmpty.orElse(second)).isEqualTo(first); + assertThat(nonEmpty.or(second).get()).isEqualTo(first); + assertThat(nonEmpty.or(() -> second).get()).isEqualTo(first); + } + + @Test + public void returnsElseValue() { + + Object reference = new Object(); + + Lazy empty = Lazy.of(() -> null); + + assertThat(empty.orElse(reference)).isEqualTo(reference); + assertThat(empty.or(reference).get()).isEqualTo(reference); + assertThat(empty.or(() -> reference).get()).isEqualTo(reference); + } }