Pooled.java
package swingtree.style;
import com.google.errorprone.annotations.Immutable;
import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
/**
* A wrapper designed for larger immutable value objects (typically config objects
* used as cache keys), which can be put to and retrieved from an internal
* object poll through its {@link #intern()} method. This is conceptually
* identical to {@link String#intern()}...<br>
* In practice this is a wrapper over a value object, which extends the value semantics
* of the thing it wraps through composition but note that at the same time it has
* its own reference identity to be used in a pool of weakly referenced objects.<br>
* <b>So although this type is treated as a value semantically
* (it overrides {@link #hashCode()} and {@link #equals(Object)})
* it still needs to be referenced in order to effectively act as a shared pointer
* and must not be converted into a full-blown value object!</b>
*
* @param <V> The type of the value object to store in an object pool
* to achieve one instance for all such values equal to each other...
*/
@Immutable
@SuppressWarnings("Immutable")
final class Pooled<V> {
private final AtomicReference<@Nullable Integer> _hashCache = new AtomicReference<>();
private final V value;
public Pooled( V value ) {
this.value = Objects.requireNonNull(value);
}
public V get() {
return this.value;
}
/**
* When the intern method is invoked, if the pool already contains a
* {@code Pooled} equal to this {@code Pooled} object as determined by
* the {@link #equals(Object)} method, then the {@code Pooled} instance
* from the pool is returned. Otherwise, this {@code Pooled} object is
* added to the pool and a reference to this {@code Pooled} object is returned.
* <p>
* It follows that for any two pooled objects {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
*
* @return a {@code Pooled} that has the same contents as this {@code Pooled}, but is
* guaranteed to be from a pool of unique {@code Pooled} instances.
*/
public Pooled<V> intern() {
return ObjectPool.get().intern(this);
}
public Pooled<V> map( Function<V, V> updater ) {
return new Pooled<>(updater.apply(value));
}
@Override
public boolean equals( Object o ) {
if (o == null || getClass() != o.getClass()) return false;
Pooled<?> other = (Pooled<?>) o;
Integer otherHash = other._hashCache.get();
Integer thisHash = this._hashCache.get();
if ( thisHash != null && otherHash != null ) {
if ( !Objects.equals(thisHash, otherHash) )
return false;
}
return Objects.equals(this.value, other.value);
}
@Override
public int hashCode() {
return Objects.requireNonNull(
_hashCache.updateAndGet(h -> h != null ? h : Objects.hashCode(value))
);
}
@Override
public String toString() {
return "PooledObject[" + "value=" + value + ']';
}
}