This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch v4 in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 0fe7edc2929f5ca22bfa264752c158186540ab3f Author: Andi Huber <[email protected]> AuthorDate: Fri Sep 26 09:18:35 2025 +0200 CAUSEWAY-3905: flesh out _StableValue object contracts --- .../commons/internal/base/_StableValue.java | 46 ++++++++++++++--- .../commons/internal/base/StableValueTest.java | 57 ++++++++++++++++++++++ 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/commons/src/main/java/org/apache/causeway/commons/internal/base/_StableValue.java b/commons/src/main/java/org/apache/causeway/commons/internal/base/_StableValue.java index 2f46a325252..1f97402a992 100644 --- a/commons/src/main/java/org/apache/causeway/commons/internal/base/_StableValue.java +++ b/commons/src/main/java/org/apache/causeway/commons/internal/base/_StableValue.java @@ -18,7 +18,10 @@ */ package org.apache.causeway.commons.internal.base; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -26,13 +29,17 @@ * <h1>- internal use only -</h1> * <p><b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/> * These may be changed or removed without notice! - * <p> - * A thread-safe, lazily initialized holder that can be set at most once. - * <p> - * This class mimics the behavior of the StableValue API introduced in JDK 25 preview. + * + * <p>A thread-safe, lazily initialized holder that can be set at most once. + * + * <p>This class mimics the behavior of the StableValue API introduced in JDK 25 preview. * It ensures that a value is set only once, using a supplier, and subsequent calls return the same value. * Useful for caching expensive computations or initializing resources lazily and safely. * + * <p>_StableValue is serializable, but such that we don't marshall the contained value, if any. + * + * <p>Equality: 2 _StableValue(s) are equal iff their contained values are equal. + * * @param <T> the type of the value to be held * @since 4.0 */ @@ -42,7 +49,7 @@ public _StableValue() { this(new AtomicReference<>()); } - public _StableValue(T t) { + public _StableValue(final T t) { this(new AtomicReference<>(t)); } @@ -54,7 +61,7 @@ public _StableValue(T t) { * @param supplier the supplier to initialize the value * @return the stored value */ - public T orElseSet(Supplier<T> supplier) { + public T orElseSet(final Supplier<T> supplier) { T value = ref.get(); if (value == null) { T newValue = supplier.get(); @@ -91,4 +98,31 @@ public T get() { return value; } + // -- CONTRACT + + @Override + public final boolean equals(final Object arg0) { + return arg0 instanceof _StableValue other + ? Objects.equals(this.ref.get(), other.ref.get()) + : false; + } + + // -- SERIALIZATION PROXY + + private final static class StableValueProxy implements Serializable { + private static final long serialVersionUID = 1L; + StableValueProxy() {} + private Object readResolve() { + return new _StableValue<>(); + } + } + + private Object writeReplace() { + return new StableValueProxy(); + } + + private void readObject(final ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + } \ No newline at end of file diff --git a/commons/src/test/java/org/apache/causeway/commons/internal/base/StableValueTest.java b/commons/src/test/java/org/apache/causeway/commons/internal/base/StableValueTest.java new file mode 100644 index 00000000000..204e9843b7c --- /dev/null +++ b/commons/src/test/java/org/apache/causeway/commons/internal/base/StableValueTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.causeway.commons.internal.base; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import org.apache.causeway.commons.internal.testing._SerializationTester; + +class StableValueTest { + + @Test + void equality_whenEmpty() { + assertEquals(new _StableValue<>(), new _StableValue<>()); + } + + @Test + void equality_whenNotEmpty() { + assertEquals(new _StableValue<>("test"), new _StableValue<>("test")); + } + + @Test + void nonEquality_whenDiffer() { + assertNotEquals(new _StableValue<>("a"), new _StableValue<>("b")); + } + + @Test + void shouldRountrip_whenEmpty() { + _SerializationTester.assertEqualsOnRoundtrip(new _StableValue<>()); + } + + @Test + void shouldBeEmpty_whenRoundtrip() { + assertEquals( + new _StableValue<>(), + _SerializationTester.roundtrip(new _StableValue<String>("test"))); + } + +}
