1 /*
2 * Copyright 2016-2020 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.springframework.data.util;
17
18 import lombok.AccessLevel;
19 import lombok.AllArgsConstructor;
20 import lombok.EqualsAndHashCode;
21 import lombok.RequiredArgsConstructor;
22
23 import java.util.Optional;
24 import java.util.function.Function;
25 import java.util.function.Supplier;
26
27 import org.springframework.lang.Nullable;
28 import org.springframework.util.Assert;
29
30 /**
31 * Simple value type to delay the creation of an object using a {@link Supplier} returning the produced object for
32 * subsequent lookups. Note, that no concurrency control is applied during the lookup of {@link #get()}, which means in
33 * concurrent access scenarios, the provided {@link Supplier} can be called multiple times.
34 *
35 * @author Oliver Gierke
36 * @author Mark Paluch
37 * @author Henning Rohlfs
38 * @since 2.0
39 */
40 @RequiredArgsConstructor
41 @AllArgsConstructor(access = AccessLevel.PRIVATE)
42 @EqualsAndHashCode
43 public class Lazy<T> implements Supplier<T> {
44
45 private static final Lazy<?> EMPTY = new Lazy<>(() -> null, null, true);
46
47 private final Supplier<? extends T> supplier;
48 private @Nullable T value = null;
49 private volatile boolean resolved = false;
50
51 /**
52 * Creates a new {@link Lazy} to produce an object lazily.
53 *
54 * @param <T> the type of which to produce an object of eventually.
55 * @param supplier the {@link Supplier} to create the object lazily.
56 * @return
57 */
58 public static <T> Lazy<T> of(Supplier<? extends T> supplier) {
59 return new Lazy<>(supplier);
60 }
61
62 /**
63 * Creates a new {@link Lazy} to return the given value.
64 *
65 * @param <T> the type of the value to return eventually.
66 * @param value the value to return.
67 * @return
68 */
69 public static <T> Lazy<T> of(T value) {
70
71 Assert.notNull(value, "Value must not be null!");
72
73 return new Lazy<>(() -> value);
74 }
75
76 /**
77 * Creates a pre-resolved empty {@link Lazy}.
78 *
79 * @return
80 * @since 2.1
81 */
82 @SuppressWarnings("unchecked")
83 public static <T> Lazy<T> empty() {
84 return (Lazy<T>) EMPTY;
85 }
86
87 /**
88 * Returns the value created by the configured {@link Supplier}. Will return the calculated instance for subsequent
89 * lookups.
90 *
91 * @return
92 */
93 public T get() {
94
95 T value = getNullable();
96
97 if (value == null) {
98 throw new IllegalStateException("Expected lazy evaluation to yield a non-null value but got null!");
99 }
100
101 return value;
102 }
103
104 /**
105 * Returns the {@link Optional} value created by the configured {@link Supplier}, allowing the absence of values in
106 * contrast to {@link #get()}. Will return the calculated instance for subsequent lookups.
107 *
108 * @return
109 */
110 public Optional<T> getOptional() {
111 return Optional.ofNullable(getNullable());
112 }
113
114 /**
115 * Returns a new Lazy that will consume the given supplier in case the current one does not yield in a result.
116 *
117 * @param supplier must not be {@literal null}.
118 * @return
119 */
120 public Lazy<T> or(Supplier<? extends T> supplier) {
121
122 Assert.notNull(supplier, "Supplier must not be null!");
123
124 return Lazy.of(() -> orElseGet(supplier));
125 }
126
127 /**
128 * Returns a new Lazy that will return the given value in case the current one does not yield in a result.
129 *
130 * @param supplier must not be {@literal null}.
131 * @return
132 */
133 public Lazy<T> or(T value) {
134
135 Assert.notNull(value, "Value must not be null!");
136
137 return Lazy.of(() -> orElse(value));
138 }
139
140 /**
141 * Returns the value of the lazy computation or the given default value in case the computation yields
142 * {@literal null}.
143 *
144 * @param value
145 * @return
146 */
147 @Nullable
148 public T orElse(@Nullable T value) {
149
150 T nullable = getNullable();
151
152 return nullable == null ? value : nullable;
153 }
154
155 /**
156 * Returns the value of the lazy computation or the value produced by the given {@link Supplier} in case the original
157 * value is {@literal null}.
158 *
159 * @param supplier must not be {@literal null}.
160 * @return
161 */
162 @Nullable
163 private T orElseGet(Supplier<? extends T> supplier) {
164
165 Assert.notNull(supplier, "Default value supplier must not be null!");
166
167 T value = getNullable();
168
169 return value == null ? supplier.get() : value;
170 }
171
172 /**
173 * Creates a new {@link Lazy} with the given {@link Function} lazily applied to the current one.
174 *
175 * @param function must not be {@literal null}.
176 * @return
177 */
178 public <S> Lazy<S> map(Function<? super T, ? extends S> function) {
179
180 Assert.notNull(function, "Function must not be null!");
181
182 return Lazy.of(() -> function.apply(get()));
183 }
184
185 /**
186 * Creates a new {@link Lazy} with the given {@link Function} lazily applied to the current one.
187 *
188 * @param function must not be {@literal null}.
189 * @return
190 */
191 public <S> Lazy<S> flatMap(Function<? super T, Lazy<? extends S>> function) {
192
193 Assert.notNull(function, "Function must not be null!");
194
195 return Lazy.of(() -> function.apply(get()).get());
196 }
197
198 /**
199 * Returns the value of the lazy evaluation.
200 *
201 * @return
202 * @since 2.2
203 */
204 @Nullable
205 public T getNullable() {
206
207 if (resolved) {
208 return value;
209 }
210
211 this.value = supplier.get();
212 this.resolved = true;
213
214 return value;
215 }
216 }
217