001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.concurrent; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022import java.util.concurrent.atomic.*; 023import java.util.function.*; 024 025/** 026 * A thread-safe reference that combines {@link AtomicReference} with {@link Optional}-like functionality. 027 * 028 * <p> 029 * This class extends {@link AtomicReference} to provide both atomic reference operations and Optional-like 030 * convenience methods for working with potentially null values. It is thread-safe and can be used in 031 * concurrent environments where you need to atomically update a reference while also having Optional-like 032 * operations. 033 * 034 * <h5 class='section'>Features:</h5> 035 * <ul class='spaced-list'> 036 * <li>Extends AtomicReference - inherits all atomic operations (get, set, compareAndSet, etc.) 037 * <li>Optional-like API - provides isPresent(), isEmpty(), map(), orElse(), etc. 038 * <li>Thread-safe - all operations are atomic 039 * <li>Null-safe - handles null values gracefully (null represents "empty") 040 * </ul> 041 * 042 * <h5 class='section'>Usage:</h5> 043 * <p class='bjava'> 044 * <jc>// Create an empty reference</jc> 045 * NullableReference<String> <jv>ref</jv> = NullableReference.<jsm>empty</jsm>(); 046 * 047 * <jc>// Set a value atomically</jc> 048 * <jv>ref</jv>.set(<js>"value"</js>); 049 * 050 * <jc>// Check if value is present</jc> 051 * <jk>if</jk> (<jv>ref</jv>.isPresent()) { 052 * String <jv>value</jv> = <jv>ref</jv>.get(); 053 * } 054 * 055 * <jc>// Use Optional-like methods</jc> 056 * String <jv>result</jv> = <jv>ref</jv>.map(String::toUpperCase).orElse(<js>"default"</js>); 057 * 058 * <jc>// Atomic update</jc> 059 * <jv>ref</jv>.compareAndSet(<js>"value"</js>, <js>"newValue"</js>); 060 * </p> 061 * 062 * <h5 class='section'>Thread Safety:</h5> 063 * <p> 064 * This class is thread-safe. All operations inherit thread-safety from {@link AtomicReference}. 065 * Multiple threads can safely call methods like {@link #get()}, {@link #set(Object)}, {@link #compareAndSet(Object, Object)}, 066 * and the Optional-like methods concurrently. 067 * 068 * <h5 class='section'>See Also:</h5><ul> 069 * <li class='jc'>{@link AtomicReference} - Base class providing atomic operations 070 * <li class='jc'>{@link Optional} - Java's Optional class 071 * <li class='jc'>{@link org.apache.juneau.commons.function.NullableSupplier} - Similar functionality for suppliers 072 * </ul> 073 * 074 * @param <V> The type of value held by this reference. 075 */ 076@SuppressWarnings({ 077 "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants 078}) 079public class NullableReference<V> extends AtomicReference<V> { 080 081 private static final long serialVersionUID = 1L; 082 083 /** 084 * Creates an empty NullableReference with an initial value of <jk>null</jk>. 085 * 086 * @param <V> The value type. 087 * @return A new empty NullableReference. 088 */ 089 public static <V> NullableReference<V> empty() { 090 return new NullableReference<>(null); 091 } 092 093 /** 094 * Creates an NullableReference with the specified initial value. 095 * 096 * @param <V> The value type. 097 * @param initialValue The initial value. Can be <jk>null</jk>. 098 * @return A new NullableReference with the specified value. 099 */ 100 public static <V> NullableReference<V> of(V initialValue) { 101 return new NullableReference<>(initialValue); 102 } 103 104 /** 105 * Creates an NullableReference with the specified initial value (alias for {@link #of(Object)}). 106 * 107 * @param <V> The value type. 108 * @param initialValue The initial value. Can be <jk>null</jk>. 109 * @return A new NullableReference with the specified value. 110 */ 111 public static <V> NullableReference<V> ofNullable(V initialValue) { 112 return new NullableReference<>(initialValue); 113 } 114 115 /** 116 * Constructor with initial value. 117 * 118 * @param initialValue The initial value. Can be <jk>null</jk>. 119 */ 120 public NullableReference(V initialValue) { 121 super(initialValue); 122 } 123 124 /** 125 * Default constructor (initializes to <jk>null</jk>). 126 */ 127 public NullableReference() { 128 super(); 129 } 130 131 /** 132 * Returns <jk>true</jk> if the current value is not <jk>null</jk>. 133 * 134 * @return <jk>true</jk> if the current value is not <jk>null</jk>. 135 */ 136 public boolean isPresent() { 137 return nn(get()); 138 } 139 140 /** 141 * Returns <jk>true</jk> if the current value is <jk>null</jk>. 142 * 143 * @return <jk>true</jk> if the current value is <jk>null</jk>. 144 */ 145 public boolean isEmpty() { 146 return !isPresent(); 147 } 148 149 /** 150 * If a value is present, applies the provided mapping function to it and returns an NullableReference describing the result. 151 * 152 * @param <U> The type of the result of the mapping function. 153 * @param mapper A mapping function to apply to the value, if present. Must not be <jk>null</jk>. 154 * @return An NullableReference describing the result of applying a mapping function to the value, if a value is present, otherwise an empty NullableReference. 155 */ 156 public <U> NullableReference<U> map(Function<? super V,? extends U> mapper) { 157 reqnn("mapper", mapper); 158 V value = get(); 159 return nn(value) ? NullableReference.of(mapper.apply(value)) : NullableReference.empty(); 160 } 161 162 /** 163 * If a value is present, returns the result of applying the given NullableReference-bearing mapping function to the value, otherwise returns an empty NullableReference. 164 * 165 * @param <U> The type parameter to the NullableReference returned by the mapping function. 166 * @param mapper A mapping function to apply to the value, if present. Must not be <jk>null</jk>. 167 * @return The result of applying an NullableReference-bearing mapping function to the value, if a value is present, otherwise an empty NullableReference. 168 */ 169 public <U> NullableReference<U> flatMap(Function<? super V,? extends NullableReference<? extends U>> mapper) { 170 reqnn("mapper", mapper); 171 V value = get(); 172 if (nn(value)) { 173 NullableReference<? extends U> result = mapper.apply(value); 174 return result != null ? NullableReference.ofNullable(result.get()) : NullableReference.empty(); 175 } 176 return NullableReference.empty(); 177 } 178 179 /** 180 * If a value is present, and the value matches the given predicate, returns an NullableReference describing the value, otherwise returns an empty NullableReference. 181 * 182 * @param predicate A predicate to apply to the value, if present. Must not be <jk>null</jk>. 183 * @return An NullableReference describing the value if a value is present and the value matches the given predicate, otherwise an empty NullableReference. 184 */ 185 public NullableReference<V> filter(Predicate<? super V> predicate) { 186 reqnn("predicate", predicate); 187 V value = get(); 188 return (nn(value) && predicate.test(value)) ? NullableReference.of(value) : NullableReference.empty(); 189 } 190 191 /** 192 * If a value is present, returns the value, otherwise returns <jk>other</jk>. 193 * 194 * @param other The value to be returned if there is no value present. Can be <jk>null</jk>. 195 * @return The value, if present, otherwise <jk>other</jk>. 196 */ 197 public V orElse(V other) { 198 V value = get(); 199 return nn(value) ? value : other; 200 } 201 202 /** 203 * If a value is present, returns the value, otherwise returns the result produced by the supplying function. 204 * 205 * @param other A {@link Supplier} whose result is returned if no value is present. Must not be <jk>null</jk>. 206 * @return The value, if present, otherwise the result of <jk>other.get()</jk>. 207 */ 208 public V orElseGet(Supplier<? extends V> other) { 209 reqnn("other", other); 210 V value = get(); 211 return nn(value) ? value : other.get(); 212 } 213 214 /** 215 * If a value is present, returns the value, otherwise throws an exception produced by the exception supplying function. 216 * 217 * @param <X> Type of the exception to be thrown. 218 * @param exceptionSupplier The supplying function that produces an exception to be thrown. Must not be <jk>null</jk>. 219 * @return The value, if present. 220 * @throws X If no value is present. 221 */ 222 public <X extends Throwable> V orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { 223 reqnn("exceptionSupplier", exceptionSupplier); 224 V value = get(); 225 if (nn(value)) 226 return value; 227 throw exceptionSupplier.get(); 228 } 229 230 /** 231 * If a value is present, performs the given action with the value, otherwise does nothing. 232 * 233 * @param action The action to be performed, if a value is present. Must not be <jk>null</jk>. 234 */ 235 public void ifPresent(Consumer<? super V> action) { 236 reqnn("action", action); 237 V value = get(); 238 if (nn(value)) 239 action.accept(value); 240 } 241 242 /** 243 * If a value is present, performs the given action with the value, otherwise performs the given empty-based action. 244 * 245 * @param action The action to be performed, if a value is present. Must not be <jk>null</jk>. 246 * @param emptyAction The empty-based action to be performed, if no value is present. Must not be <jk>null</jk>. 247 */ 248 public void ifPresentOrElse(Consumer<? super V> action, Runnable emptyAction) { 249 reqnn("action", action); 250 reqnn("emptyAction", emptyAction); 251 V value = get(); 252 if (nn(value)) 253 action.accept(value); 254 else 255 emptyAction.run(); 256 } 257 258 /** 259 * Converts this NullableReference to an {@link Optional}. 260 * 261 * @return An Optional containing the value if present, otherwise an empty Optional. 262 */ 263 public Optional<V> toOptional() { 264 return o(get()); 265 } 266}