Skip to content

Commit 01e85c9

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 01e85c9

File tree

4 files changed

+227
-51
lines changed

4 files changed

+227
-51
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 & 51 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,13 @@
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);
43+
private static final String UNRESOLVED = "[Unresolved]";
4544

46-
private final Supplier<? extends @Nullable T> supplier;
45+
private final LazyDelegate<? extends @Nullable T> adapter;
4746

48-
private @Nullable T value;
49-
private volatile boolean resolved;
50-
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;
47+
private Lazy(Supplier<? extends @Nullable T> adapter) {
48+
this.adapter = new LazyDelegate<>(adapter);
6749
}
6850

6951
/**
@@ -128,16 +110,7 @@ public T get() {
128110
*/
129111
@Nullable
130112
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;
113+
return adapter.getNullable();
141114
}
142115

143116
/**
@@ -249,35 +222,22 @@ public boolean equals(@Nullable Object o) {
249222
return false;
250223
}
251224

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);
225+
return adapter.equals(lazy.adapter);
261226
}
262227

263228
@Override
264229
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;
230+
return adapter.hashCode();
272231
}
273232

274233
@Override
275234
public String toString() {
276235

277-
if (!resolved) {
236+
if (!adapter.isResolved()) {
278237
return UNRESOLVED;
279238
}
280239

240+
T value = getNullable();
281241
return value == null ? "null" : value.toString();
282242
}
283243

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

294254
Assert.notNull(fallback, "Fallback must not be null!");
295255

296-
return resolved ? toString() : fallback.get();
256+
return adapter.isResolved() ? toString() : fallback.get();
297257
}
298258
}
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)