Skip to content

Commit cd55251

Browse files
committed
Use StableValue behind Lazy.
We now use Java's StableValue to back our Lazy implementation. StableValue suggests an Optional orElse…(…) usage pattern and it is not primarily intended for lazifying value resolution, therefore we have to mimic StableValue.supplier(…).
1 parent deb1b4f commit cd55251

File tree

4 files changed

+227
-50
lines changed

4 files changed

+227
-50
lines changed

pom.xml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,18 +341,55 @@
341341

342342
<build>
343343
<plugins>
344+
344345
<plugin>
345346
<groupId>org.apache.maven.plugins</groupId>
346347
<artifactId>maven-assembly-plugin</artifactId>
347348
</plugin>
349+
348350
<plugin>
349351
<groupId>org.antora</groupId>
350352
<artifactId>antora-maven-plugin</artifactId>
351353
</plugin>
354+
355+
<plugin>
356+
<groupId>org.apache.maven.plugins</groupId>
357+
<artifactId>maven-jar-plugin</artifactId>
358+
<executions>
359+
<execution>
360+
<id>default-jar</id>
361+
<configuration>
362+
<archive>
363+
<manifestEntries>
364+
<Multi-Release>true</Multi-Release>
365+
</manifestEntries>
366+
</archive>
367+
</configuration>
368+
</execution>
369+
</executions>
370+
</plugin>
371+
352372
<plugin>
353373
<groupId>org.apache.maven.plugins</groupId>
354374
<artifactId>maven-compiler-plugin</artifactId>
355375
<executions>
376+
377+
<execution>
378+
<id>jdk25</id>
379+
<goals>
380+
<goal>compile</goal>
381+
</goals>
382+
<configuration>
383+
<enablePreview>true</enablePreview>
384+
<release>25</release>
385+
<compileSourceRoots>
386+
<compileSourceRoot>${project.basedir}/src/main/java25
387+
</compileSourceRoot>
388+
</compileSourceRoots>
389+
<multiReleaseOutput>true</multiReleaseOutput>
390+
</configuration>
391+
</execution>
392+
356393
<execution>
357394
<id>java-test-compile</id>
358395
<configuration>
@@ -366,8 +403,10 @@
366403
</annotationProcessorPaths>
367404
</configuration>
368405
</execution>
406+
369407
</executions>
370408
</plugin>
409+
371410
</plugins>
372411
</build>
373412

src/main/java/org/springframework/data/util/Lazy.java

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.jspecify.annotations.Nullable;
2323

2424
import org.springframework.util.Assert;
25-
import org.springframework.util.ObjectUtils;
2625

2726
/**
2827
* Simple value type to delay the creation of an object using a {@link Supplier} returning the produced object for
@@ -40,30 +39,14 @@
4039
*/
4140
public class Lazy<T> implements Supplier<T> {
4241

43-
private static final Lazy<?> EMPTY = new Lazy<>(() -> null, null, true);
44-
static final String UNRESOLVED = "[Unresolved]";
42+
private static final Lazy<?> EMPTY = new Lazy<>(() -> null);
4543

46-
private final Supplier<? extends @Nullable T> supplier;
44+
static final String UNRESOLVED = "[Unresolved]";
4745

48-
private @Nullable T value;
49-
private volatile boolean resolved;
46+
private final LazyDelegate<? extends @Nullable T> adapter;
5047

51-
private Lazy(Supplier<? extends @Nullable T> supplier) {
52-
this(supplier, null, false);
53-
}
54-
55-
/**
56-
* Creates a new {@code Lazy} for the given {@link Supplier}, value and whether it has been resolved or not.
57-
*
58-
* @param supplier must not be {@literal null}.
59-
* @param value can be {@literal null}.
60-
* @param resolved whether the value handed into the constructor represents a resolved value.
61-
*/
62-
private Lazy(Supplier<? extends @Nullable T> supplier, @Nullable T value, boolean resolved) {
63-
64-
this.supplier = supplier;
65-
this.value = value;
66-
this.resolved = resolved;
48+
private Lazy(Supplier<? extends @Nullable T> adapter) {
49+
this.adapter = new LazyDelegate<>(adapter);
6750
}
6851

6952
/**
@@ -128,16 +111,7 @@ public T get() {
128111
*/
129112
@Nullable
130113
public T getNullable() {
131-
132-
if (resolved) {
133-
return value;
134-
}
135-
136-
T result = supplier.get();
137-
this.value = result;
138-
this.resolved = true;
139-
140-
return result;
114+
return adapter.getNullable();
141115
}
142116

143117
/**
@@ -249,35 +223,22 @@ public boolean equals(@Nullable Object o) {
249223
return false;
250224
}
251225

252-
if (resolved != lazy.resolved) {
253-
return false;
254-
}
255-
256-
if (!ObjectUtils.nullSafeEquals(supplier, lazy.supplier)) {
257-
return false;
258-
}
259-
260-
return ObjectUtils.nullSafeEquals(value, lazy.value);
226+
return adapter.equals(lazy.adapter);
261227
}
262228

263229
@Override
264230
public int hashCode() {
265-
266-
int result = ObjectUtils.nullSafeHashCode(supplier);
267-
268-
result = 31 * result + ObjectUtils.nullSafeHashCode(value);
269-
result = 31 * result + (resolved ? 1 : 0);
270-
271-
return result;
231+
return adapter.hashCode();
272232
}
273233

274234
@Override
275235
public String toString() {
276236

277-
if (!resolved) {
237+
if (!adapter.isResolved()) {
278238
return UNRESOLVED;
279239
}
280240

241+
T value = getNullable();
281242
return value == null ? "null" : value.toString();
282243
}
283244

@@ -293,6 +254,6 @@ public String toString(Supplier<String> fallback) {
293254

294255
Assert.notNull(fallback, "Fallback must not be null!");
295256

296-
return resolved ? toString() : fallback.get();
257+
return adapter.isResolved() ? toString() : fallback.get();
297258
}
298259
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2016-2025 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 java.util.function.Supplier;
19+
20+
import org.jspecify.annotations.Nullable;
21+
22+
import org.springframework.util.ObjectUtils;
23+
24+
/**
25+
* Simple lazy implementation using lazy value resolution. Lazy evaluation not guarded with locks and can therefore lead
26+
* to multiple invocations of the underlying {@link Supplier}.
27+
*
28+
* @author Oliver Gierke
29+
* @author Mark Paluch
30+
* @author Henning Rohlfs
31+
* @author Johannes Englmeier
32+
* @author Greg Turnquist
33+
* @since 4.0
34+
*/
35+
class LazyDelegate<T extends @Nullable Object> {
36+
37+
private final Supplier<T> supplier;
38+
39+
private @Nullable T value;
40+
private volatile boolean resolved;
41+
42+
LazyDelegate(Supplier<T> supplier) {
43+
this.supplier = supplier;
44+
}
45+
46+
/**
47+
* Returns the value of the lazy evaluation.
48+
*
49+
* @return the value of the lazy evaluation, can be {@literal null}.
50+
*/
51+
@Nullable
52+
public T getNullable() {
53+
54+
if (resolved) {
55+
return value;
56+
}
57+
58+
T result = supplier.get();
59+
this.value = result;
60+
this.resolved = true;
61+
62+
return result;
63+
}
64+
65+
public boolean isResolved() {
66+
return resolved;
67+
}
68+
69+
@Override
70+
public boolean equals(@Nullable Object o) {
71+
72+
if (this == o) {
73+
return true;
74+
}
75+
76+
if (!(o instanceof LazyDelegate<?> lazy)) {
77+
return false;
78+
}
79+
80+
if (resolved != lazy.resolved) {
81+
return false;
82+
}
83+
84+
if (!ObjectUtils.nullSafeEquals(supplier, lazy.supplier)) {
85+
return false;
86+
}
87+
88+
return ObjectUtils.nullSafeEquals(value, lazy.value);
89+
}
90+
91+
@Override
92+
public int hashCode() {
93+
94+
int result = ObjectUtils.nullSafeHashCode(supplier);
95+
96+
result = 31 * result + ObjectUtils.nullSafeHashCode(value);
97+
result = 31 * result + (resolved ? 1 : 0);
98+
99+
return result;
100+
}
101+
102+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2016-2025 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 java.util.function.Supplier;
19+
20+
import org.jspecify.annotations.Nullable;
21+
22+
import org.springframework.util.ObjectUtils;
23+
24+
/**
25+
* Lazy adapter using {@link StableValue}.
26+
*
27+
* @author Mark Paluch
28+
* @since 4.0
29+
*/
30+
class LazyDelegate<T extends @Nullable Object> {
31+
32+
private final Supplier<T> supplier;
33+
private final StableValue<T> value;
34+
35+
LazyDelegate(Supplier<T> supplier) {
36+
37+
System.out.println("Stable");
38+
this.supplier = supplier;
39+
this.value = StableValue.of();
40+
}
41+
42+
/**
43+
* Returns the value of the lazy evaluation.
44+
*
45+
* @return the value of the lazy evaluation, can be {@literal null}.
46+
*/
47+
@Nullable
48+
public T getNullable() {
49+
return value.orElseSet(supplier);
50+
}
51+
52+
public boolean isResolved() {
53+
return value.isSet();
54+
}
55+
56+
@Override
57+
public boolean equals(@Nullable Object o) {
58+
59+
if (this == o) {
60+
return true;
61+
}
62+
63+
if (!(o instanceof LazyDelegate<?> lazy)) {
64+
return false;
65+
}
66+
67+
return ObjectUtils.nullSafeEquals(value, lazy.value);
68+
}
69+
70+
@Override
71+
public int hashCode() {
72+
return value.hashCode();
73+
}
74+
75+
}

0 commit comments

Comments
 (0)