Add lazy-constants module: LazyConstant (JEP 531) against the holder idiom and double-checked locking
Basics, failure semantics on 26 vs 27, lazy collections, API across 25/26/27, preview-flag traps and a JMH read-path benchmark (jars fetched and sha1-checked, not committed). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TF9JWFvJSNm6HVzswzZU5a
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
$ javac src/OldWays.java && java OldWays (21.0.10+7-Ubuntu-124.04)
|
||||
[init] building Expensive via eager static final
|
||||
main starts (the eager value already exists)
|
||||
first holder() call:
|
||||
[init] building Expensive via holder idiom
|
||||
second holder() call:
|
||||
first doubleChecked() call:
|
||||
[init] building Expensive via double-checked locking
|
||||
second doubleChecked() call:
|
||||
|
||||
$ javac src/OldWays.java && java OldWays (25.0.4.1+1-LTS)
|
||||
[init] building Expensive via eager static final
|
||||
main starts (the eager value already exists)
|
||||
first holder() call:
|
||||
[init] building Expensive via holder idiom
|
||||
second holder() call:
|
||||
first doubleChecked() call:
|
||||
[init] building Expensive via double-checked locking
|
||||
second doubleChecked() call:
|
||||
|
||||
$ javac src/OldWays.java && java OldWays (27+35)
|
||||
[init] building Expensive via eager static final
|
||||
main starts (the eager value already exists)
|
||||
first holder() call:
|
||||
[init] building Expensive via holder idiom
|
||||
second holder() call:
|
||||
first doubleChecked() call:
|
||||
[init] building Expensive via double-checked locking
|
||||
second doubleChecked() call:
|
||||
@@ -0,0 +1,20 @@
|
||||
$ javac --enable-preview --release 27 src/Basics.java && java --enable-preview Basics (27+35)
|
||||
supplier calls before get: 0
|
||||
get() returns: loaded
|
||||
get() again: loaded
|
||||
supplier calls after two gets: 1
|
||||
16 threads asked at once, supplier calls: 1
|
||||
|
||||
$ javac --enable-preview --release 27 src/ToStringStates.java && java --enable-preview ToStringStates (27+35)
|
||||
before get: [computing function=ToStringStates$$Lambda]
|
||||
after get: [loaded]
|
||||
after failed get: [failed with=java.lang.IllegalStateException]
|
||||
|
||||
$ javac --enable-preview --release 27 src/Service.java && java --enable-preview Service (27+35)
|
||||
service created
|
||||
[load] reading configuration
|
||||
url: https://example.test
|
||||
url again: https://example.test
|
||||
[compile] building the e-mail pattern
|
||||
isEmail([email protected]): true
|
||||
isEmail(nope): false
|
||||
@@ -0,0 +1,13 @@
|
||||
$ javac --enable-preview --release 26 src/Failures.java && java --enable-preview Failures (26.0.2.1+1)
|
||||
get #1: java.lang.IllegalStateException: boom | cause=null | supplier calls=1
|
||||
get #2: java.lang.IllegalStateException: boom | cause=null | supplier calls=2
|
||||
get #3: java.lang.IllegalStateException: boom | cause=null | supplier calls=3
|
||||
null result: java.lang.NullPointerException | cause=null
|
||||
recursion: java.lang.IllegalStateException: Recursive invocation of a LazyConstant's computing function: Failures$$Lambda | cause=null
|
||||
|
||||
$ javac --enable-preview --release 27 src/Failures.java && java --enable-preview Failures (27+35)
|
||||
get #1: java.util.NoSuchElementException: Unable to access the constant because java.lang.IllegalStateException was thrown at initial computation | cause=java.lang.IllegalStateException: boom | supplier calls=1
|
||||
get #2: java.util.NoSuchElementException: Unable to access the constant because java.lang.IllegalStateException was thrown at initial computation | cause=null | supplier calls=1
|
||||
get #3: java.util.NoSuchElementException: Unable to access the constant because java.lang.IllegalStateException was thrown at initial computation | cause=null | supplier calls=1
|
||||
null result: java.util.NoSuchElementException: Unable to access the constant because java.lang.NullPointerException was thrown at initial computation | cause=java.lang.NullPointerException
|
||||
recursion: java.util.NoSuchElementException: Unable to access the constant because java.lang.IllegalStateException was thrown at initial computation | cause=java.lang.IllegalStateException: Recursive invocation of a LazyConstant's computing function: Failures$$Lambda
|
||||
@@ -0,0 +1,26 @@
|
||||
$ javac --enable-preview --release 27 src/LazyCollections.java && java --enable-preview LazyCollections (27+35)
|
||||
list created, size 3 (nothing computed yet)
|
||||
computing list element 1
|
||||
list.get(1) = v1
|
||||
list.get(1) again = v1
|
||||
map created, size 3 (nothing computed yet)
|
||||
computing map value for bb
|
||||
map.get("bb") = 2
|
||||
map.get("bb") again = 2
|
||||
set created
|
||||
testing 2
|
||||
set.contains(2) = true
|
||||
testing 1
|
||||
set.contains(1) = false
|
||||
set.contains(9) = false
|
||||
testing 3
|
||||
set.size() = 2
|
||||
|
||||
$ javac --enable-preview --release 26 src/LazyCollections.java (26.0.2.1+1)
|
||||
src/LazyCollections.java:18: error: cannot find symbol
|
||||
Set<Integer> set = Set.ofLazy(Set.of(1, 2, 3), n -> { System.out.println(" testing " + n); return n > 1; });
|
||||
^
|
||||
symbol: method ofLazy(Set<Integer>,(n)->{ Sys[...] 1; })
|
||||
location: interface Set
|
||||
1 error
|
||||
exit=1
|
||||
@@ -0,0 +1,118 @@
|
||||
$ javap java.lang.StableValue (25.0.4.1+1-LTS)
|
||||
Compiled from "StableValue.java"
|
||||
public interface java.lang.StableValue<T> {
|
||||
public abstract boolean trySet(T);
|
||||
public abstract T orElse(T);
|
||||
public abstract T orElseThrow();
|
||||
public abstract boolean isSet();
|
||||
public abstract T orElseSet(java.util.function.Supplier<? extends T>);
|
||||
public abstract void setOrThrow(T);
|
||||
public abstract boolean equals(java.lang.Object);
|
||||
public abstract int hashCode();
|
||||
public static <T> java.lang.StableValue<T> of();
|
||||
public static <T> java.lang.StableValue<T> of(T);
|
||||
public static <T> java.util.function.Supplier<T> supplier(java.util.function.Supplier<? extends T>);
|
||||
public static <R> java.util.function.IntFunction<R> intFunction(int, java.util.function.IntFunction<? extends R>);
|
||||
public static <T, R> java.util.function.Function<T, R> function(java.util.Set<? extends T>, java.util.function.Function<? super T, ? extends R>);
|
||||
public static <E> java.util.List<E> list(int, java.util.function.IntFunction<? extends E>);
|
||||
public static <K, V> java.util.Map<K, V> map(java.util.Set<K>, java.util.function.Function<? super K, ? extends V>);
|
||||
}
|
||||
|
||||
$ javap java.lang.LazyConstant (26.0.2.1+1)
|
||||
Compiled from "LazyConstant.java"
|
||||
public interface java.lang.LazyConstant<T> extends java.util.function.Supplier<T> {
|
||||
public abstract T orElse(T);
|
||||
public abstract T get();
|
||||
public abstract boolean isInitialized();
|
||||
public abstract boolean equals(java.lang.Object);
|
||||
public abstract int hashCode();
|
||||
public abstract java.lang.String toString();
|
||||
public static <T> java.lang.LazyConstant<T> of(java.util.function.Supplier<? extends T>);
|
||||
}
|
||||
|
||||
$ javap java.lang.LazyConstant (27+35)
|
||||
Compiled from "LazyConstant.java"
|
||||
public interface java.lang.LazyConstant<T> extends java.util.function.Supplier<T> {
|
||||
public abstract T get();
|
||||
public abstract boolean equals(java.lang.Object);
|
||||
public abstract int hashCode();
|
||||
public abstract java.lang.String toString();
|
||||
public static <T> java.lang.LazyConstant<T> of(java.util.function.Supplier<? extends T>);
|
||||
}
|
||||
|
||||
$ javap java.util.List | grep ofLazy (26.0.2.1+1)
|
||||
public static <E> java.util.List<E> ofLazy(int, java.util.function.IntFunction<? extends E>);
|
||||
$ javap java.util.Map | grep ofLazy (26.0.2.1+1)
|
||||
public static <K, V> java.util.Map<K, V> ofLazy(java.util.Set<? extends K>, java.util.function.Function<? super K, ? extends V>);
|
||||
$ javap java.util.Set | grep ofLazy (26.0.2.1+1)
|
||||
|
||||
$ javap java.util.List | grep ofLazy (27+35)
|
||||
public static <E> java.util.List<E> ofLazy(int, java.util.function.IntFunction<? extends E>);
|
||||
$ javap java.util.Map | grep ofLazy (27+35)
|
||||
public static <K, V> java.util.Map<K, V> ofLazy(java.util.Set<? extends K>, java.util.function.Function<? super K, ? extends V>);
|
||||
$ javap java.util.Set | grep ofLazy (27+35)
|
||||
public static <E> java.util.Set<E> ofLazy(java.util.Set<? extends E>, java.util.function.Predicate<? super E>);
|
||||
|
||||
$ javac --enable-preview --release 25 src/StableValue25.java && java --enable-preview StableValue25 (25.0.4.1+1-LTS)
|
||||
[load] configuration
|
||||
CONFIG.get(): loaded
|
||||
CONFIG.get(): loaded
|
||||
[compute] orElseSet
|
||||
lazy(): computed
|
||||
lazy(): computed
|
||||
[element] 2
|
||||
LIST.get(2): v2
|
||||
|
||||
$ javac --enable-preview --release 26 broken/StillStable.java (26.0.2.1+1)
|
||||
broken/StillStable.java:3: error: cannot find symbol
|
||||
static final StableValue<String> CONFIG = StableValue.of();
|
||||
^
|
||||
symbol: class StableValue
|
||||
location: class StillStable
|
||||
broken/StillStable.java:3: error: cannot find symbol
|
||||
static final StableValue<String> CONFIG = StableValue.of();
|
||||
^
|
||||
symbol: variable StableValue
|
||||
location: class StillStable
|
||||
2 errors
|
||||
exit=1
|
||||
|
||||
$ javac --enable-preview --release 27 broken/StillStable.java (27+35)
|
||||
broken/StillStable.java:3: error: cannot find symbol
|
||||
static final StableValue<String> CONFIG = StableValue.of();
|
||||
^
|
||||
symbol: class StableValue
|
||||
location: class StillStable
|
||||
broken/StillStable.java:3: error: cannot find symbol
|
||||
static final StableValue<String> CONFIG = StableValue.of();
|
||||
^
|
||||
symbol: variable StableValue
|
||||
location: class StillStable
|
||||
2 errors
|
||||
exit=1
|
||||
|
||||
$ javac --enable-preview --release 26 broken/RemovedApi.java (26.0.2.1+1)
|
||||
exit=0
|
||||
|
||||
$ javac --enable-preview --release 27 broken/RemovedApi.java (27+35)
|
||||
broken/RemovedApi.java:4: error: cannot find symbol
|
||||
System.out.println(c.isInitialized()); // exists on JDK 26, removed in 27
|
||||
^
|
||||
symbol: method isInitialized()
|
||||
location: variable c of type LazyConstant<String>
|
||||
broken/RemovedApi.java:5: error: cannot find symbol
|
||||
System.out.println(c.orElse("fallback")); // exists on JDK 26, removed in 27
|
||||
^
|
||||
symbol: method orElse(String)
|
||||
location: variable c of type LazyConstant<String>
|
||||
2 errors
|
||||
exit=1
|
||||
|
||||
$ javac --enable-preview --release 27 broken/ReassignInSupplier.java (27+35)
|
||||
broken/ReassignInSupplier.java:5: error: cannot find symbol
|
||||
c.set("y");
|
||||
^
|
||||
symbol: method set(String)
|
||||
location: variable c of type LazyConstant<String>
|
||||
1 error
|
||||
exit=1
|
||||
@@ -0,0 +1,23 @@
|
||||
$ javac broken/NoPreviewFlag.java (27+35)
|
||||
broken/NoPreviewFlag.java:3: error: LazyConstant is a preview API and is disabled by default.
|
||||
static final LazyConstant<String> CONFIG = LazyConstant.of(() -> "x");
|
||||
^
|
||||
(use --enable-preview to enable preview APIs)
|
||||
broken/NoPreviewFlag.java:3: error: LazyConstant is a preview API and is disabled by default.
|
||||
static final LazyConstant<String> CONFIG = LazyConstant.of(() -> "x");
|
||||
^
|
||||
(use --enable-preview to enable preview APIs)
|
||||
2 errors
|
||||
exit=1
|
||||
|
||||
$ javap -v Basics | grep 'major\|minor' (27+35, compiled with --enable-preview --release 27)
|
||||
minor version: 65535
|
||||
major version: 71
|
||||
|
||||
$ java Basics (27+35, without --enable-preview)
|
||||
Error: LinkageError occurred while loading main class Basics
|
||||
java.lang.UnsupportedClassVersionError: Preview features are not enabled for Basics (class file version 71.65535). Try running with '--enable-preview'
|
||||
|
||||
$ java --enable-preview Basics (26.0.2.1+1, class file built by 27)
|
||||
Error: LinkageError occurred while loading main class Basics
|
||||
java.lang.UnsupportedClassVersionError: Basics has been compiled by a more recent version of the Java Runtime (class file version 71.65535), this version of the Java Runtime only recognizes class file versions up to 70.0
|
||||
@@ -0,0 +1,9 @@
|
||||
$ java --enable-preview org.openjdk.jmh.Main -f 3 -wi 3 -w 1 -i 5 -r 1 (27+35, JMH 1.37)
|
||||
machine: 2 CPUs, Intel(R) Xeon(R) Processor @ 2.80GHz
|
||||
Benchmark Mode Cnt Score Error Units
|
||||
AccessBench.doubleCheckedLocking avgt 15 0.964 ± 0.028 ns/op
|
||||
AccessBench.eagerStaticFinal avgt 15 0.648 ± 0.018 ns/op
|
||||
AccessBench.holderIdiom avgt 15 0.639 ± 0.024 ns/op
|
||||
AccessBench.lazyConstantInstanceField avgt 15 1.465 ± 0.065 ns/op
|
||||
AccessBench.lazyConstantNonFinalStatic avgt 15 1.494 ± 0.064 ns/op
|
||||
AccessBench.lazyConstantStaticFinal avgt 15 0.636 ± 0.019 ns/op
|
||||
Reference in New Issue
Block a user