com.google.common.base.Suppliers Maven / Gradle / Ivy
/*
* Copyright (C) 2007 The Guava Authors
*
* Licensed 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 com.google.common.base;
import static com.google.common.base.NullnessCasts.uncheckedCastNullableTToT;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.VisibleForTesting;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Useful suppliers.
*
* All methods return serializable suppliers as long as they're given serializable parameters.
*
* @author Laurence Gonsalves
* @author Harry Heymann
* @since 2.0
*/
@GwtCompatible
@ElementTypesAreNonnullByDefault
public final class Suppliers {
private Suppliers() {}
/**
* Returns a new supplier which is the composition of the provided function and supplier. In other
* words, the new supplier's value will be computed by retrieving the value from {@code supplier},
* and then applying {@code function} to that value. Note that the resulting supplier will not
* call {@code supplier} or invoke {@code function} until it is called.
*/
public static Supplier compose(
Function function, Supplier supplier) {
return new SupplierComposition<>(function, supplier);
}
private static class SupplierComposition
implements Supplier, Serializable {
final Function function;
final Supplier supplier;
SupplierComposition(Function function, Supplier supplier) {
this.function = checkNotNull(function);
this.supplier = checkNotNull(supplier);
}
@Override
@ParametricNullness
public T get() {
return function.apply(supplier.get());
}
@Override
public boolean equals(@CheckForNull Object obj) {
if (obj instanceof SupplierComposition) {
SupplierComposition that = (SupplierComposition) obj;
return function.equals(that.function) && supplier.equals(that.supplier);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(function, supplier);
}
@Override
public String toString() {
return "Suppliers.compose(" + function + ", " + supplier + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
* and returns that value on subsequent calls to {@code get()}. See: memoization
*
* The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
* most once unless the underlying {@code get()} throws an exception. The supplier's serialized
* form does not contain the cached value, which will be recalculated when {@code get()} is called
* on the deserialized instance.
*
*
When the underlying delegate throws an exception then this memoizing supplier will keep
* delegating calls until it returns valid data.
*
*
If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
* returned directly.
*/
public static Supplier memoize(Supplier delegate) {
if (delegate instanceof NonSerializableMemoizingSupplier
|| delegate instanceof MemoizingSupplier) {
return delegate;
}
return delegate instanceof Serializable
? new MemoizingSupplier(delegate)
: new NonSerializableMemoizingSupplier(delegate);
}
@VisibleForTesting
static class MemoizingSupplier implements Supplier, Serializable {
final Supplier delegate;
transient volatile boolean initialized;
// "value" does not need to be volatile; visibility piggy-backs
// on volatile read of "initialized".
@CheckForNull transient T value;
MemoizingSupplier(Supplier delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
@ParametricNullness
public T get() {
// A 2-field variant of Double Checked Locking.
if (!initialized) {
synchronized (this) {
if (!initialized) {
T t = delegate.get();
value = t;
initialized = true;
return t;
}
}
}
// This is safe because we checked `initialized.`
return uncheckedCastNullableTToT(value);
}
@Override
public String toString() {
return "Suppliers.memoize("
+ (initialized ? "" : delegate)
+ ")";
}
private static final long serialVersionUID = 0;
}
@VisibleForTesting
static class NonSerializableMemoizingSupplier implements Supplier {
@SuppressWarnings("UnnecessaryLambda") // Must be a fixed singleton object
private static final Supplier SUCCESSFULLY_COMPUTED =
() -> {
throw new IllegalStateException(); // Should never get called.
};
private volatile Supplier delegate;
// "value" does not need to be volatile; visibility piggy-backs on volatile read of "delegate".
@CheckForNull private T value;
NonSerializableMemoizingSupplier(Supplier delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
@ParametricNullness
@SuppressWarnings("unchecked") // Cast from Supplier to Supplier is always valid
public T get() {
// Because Supplier is read-heavy, we use the "double-checked locking" pattern.
if (delegate != SUCCESSFULLY_COMPUTED) {
synchronized (this) {
if (delegate != SUCCESSFULLY_COMPUTED) {
T t = delegate.get();
value = t;
delegate = (Supplier) SUCCESSFULLY_COMPUTED;
return t;
}
}
}
// This is safe because we checked `delegate.`
return uncheckedCastNullableTToT(value);
}
@Override
public String toString() {
Supplier delegate = this.delegate;
return "Suppliers.memoize("
+ (delegate == SUCCESSFULLY_COMPUTED
? ""
: delegate)
+ ")";
}
}
/**
* Returns a supplier that caches the instance supplied by the delegate and removes the cached
* value after the specified time has passed. Subsequent calls to {@code get()} return the cached
* value if the expiration time has not passed. After the expiration time, a new value is
* retrieved, cached, and returned. See: memoization
*
* The returned supplier is thread-safe. The supplier's serialized form does not contain the
* cached value, which will be recalculated when {@code get()} is called on the reserialized
* instance. The actual memoization does not happen when the underlying delegate throws an
* exception.
*
*
When the underlying delegate throws an exception then this memoizing supplier will keep
* delegating calls until it returns valid data.
*
* @param duration the length of time after a value is created that it should stop being returned
* by subsequent {@code get()} calls
* @param unit the unit that {@code duration} is expressed in
* @throws IllegalArgumentException if {@code duration} is not positive
* @since 2.0
*/
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static Supplier memoizeWithExpiration(
Supplier delegate, long duration, TimeUnit unit) {
return new ExpiringMemoizingSupplier<>(delegate, duration, unit);
}
@VisibleForTesting
@SuppressWarnings("GoodTime") // lots of violations
static class ExpiringMemoizingSupplier
implements Supplier, Serializable {
final Supplier delegate;
final long durationNanos;
@CheckForNull transient volatile T value;
// The special value 0 means "not yet initialized".
transient volatile long expirationNanos;
ExpiringMemoizingSupplier(Supplier delegate, long duration, TimeUnit unit) {
this.delegate = checkNotNull(delegate);
this.durationNanos = unit.toNanos(duration);
checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit);
}
@Override
@ParametricNullness
@SuppressWarnings("GoodTime") // reading system time without TimeSource
public T get() {
// Another variant of Double Checked Locking.
//
// We use two volatile reads. We could reduce this to one by
// putting our fields into a holder class, but (at least on x86)
// the extra memory consumption and indirection are more
// expensive than the extra volatile reads.
long nanos = expirationNanos;
long now = System.nanoTime();
if (nanos == 0 || now - nanos >= 0) {
synchronized (this) {
if (nanos == expirationNanos) { // recheck for lost race
T t = delegate.get();
value = t;
nanos = now + durationNanos;
// In the very unlikely event that nanos is 0, set it to 1;
// no one will notice 1 ns of tardiness.
expirationNanos = (nanos == 0) ? 1 : nanos;
return t;
}
}
}
// This is safe because we checked `expirationNanos.`
return uncheckedCastNullableTToT(value);
}
@Override
public String toString() {
// This is a little strange if the unit the user provided was not NANOS,
// but we don't want to store the unit just for toString
return "Suppliers.memoizeWithExpiration(" + delegate + ", " + durationNanos + ", NANOS)";
}
private static final long serialVersionUID = 0;
}
/** Returns a supplier that always supplies {@code instance}. */
public static Supplier ofInstance(
@ParametricNullness T instance) {
return new SupplierOfInstance<>(instance);
}
private static class SupplierOfInstance
implements Supplier, Serializable {
@ParametricNullness final T instance;
SupplierOfInstance(@ParametricNullness T instance) {
this.instance = instance;
}
@Override
@ParametricNullness
public T get() {
return instance;
}
@Override
public boolean equals(@CheckForNull Object obj) {
if (obj instanceof SupplierOfInstance) {
SupplierOfInstance that = (SupplierOfInstance) obj;
return Objects.equal(instance, that.instance);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(instance);
}
@Override
public String toString() {
return "Suppliers.ofInstance(" + instance + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a supplier whose {@code get()} method synchronizes on {@code delegate} before calling
* it, making it thread-safe.
*/
public static Supplier synchronizedSupplier(
Supplier delegate) {
return new ThreadSafeSupplier<>(delegate);
}
private static class ThreadSafeSupplier
implements Supplier, Serializable {
final Supplier delegate;
ThreadSafeSupplier(Supplier delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
@ParametricNullness
public T get() {
synchronized (delegate) {
return delegate.get();
}
}
@Override
public String toString() {
return "Suppliers.synchronizedSupplier(" + delegate + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a function that accepts a supplier and returns the result of invoking {@link
* Supplier#get} on that supplier.
*
* Java 8 users: use the method reference {@code Supplier::get} instead.
*
* @since 8.0
*/
public static Function, T> supplierFunction() {
@SuppressWarnings("unchecked") // implementation is "fully variant"
SupplierFunction sf = (SupplierFunction) SupplierFunctionImpl.INSTANCE;
return sf;
}
private interface SupplierFunction extends Function, T> {}
private enum SupplierFunctionImpl implements SupplierFunction< Object> {
INSTANCE;
// Note: This makes T a "pass-through type"
@Override
@CheckForNull
public Object apply(Supplier< Object> input) {
return input.get();
}
@Override
public String toString() {
return "Suppliers.supplierFunction()";
}
}
}