提示:自定义java1.8Optional 同步新功能
前言
`经常使用Optional的小伙伴知道,Optional是java1.8引入的判空工具,网上关于Optional使用的方法有
很多,但是我们知道在java1.8以后Optional新增了一些api(如jdk1.9),新增了一些实用的功能,本文就是自定义了一个Optional类 新增部分api(copy 自 jdk1.9) 包括api:
ifPresentOrElse()
or()
提示:以下是本篇文章正文内容,下面案例可供参考
一、代码示例
package com.bonree.rum.common.util;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* @author :pys
* @时间:2023年02月23日 14:35
* @说明:copy自jdk1.9Optional新增部分api
*/
public class CustomOptional<T> {
/**
* Common instance for {@code empty()}.
*/
private static final CustomOptional<?> EMPTY = new CustomOptional<>(null);
/**
* If non-null, the value; if null, indicates no value is present
*/
private final T value;
/**
* Returns an empty {@code CustomOptional} instance. No value is present for this
* {@code CustomOptional}.
*
* @apiNote
* Though it may be tempting to do so, avoid testing if an object is empty
* by comparing with {@code ==} or {@code !=} against instances returned by
* {@code CustomOptional.empty()}. There is no guarantee that it is a singleton.
* Instead, use {@link #isEmpty()} or {@link #isPresent()}.
*
* @param <T> The type of the non-existent value
* @return an empty {@code CustomOptional}
*/
public static<T> CustomOptional<T> empty() {
@SuppressWarnings("unchecked")
CustomOptional<T> t = (CustomOptional<T>) EMPTY;
return t;
}
/**
* Constructs an instance with the described value.
*
* @param value the value to describe; it's the caller's responsibility to
* ensure the value is non-{@code null} unless creating the singleton
* instance returned by {@code empty()}.
*/
private CustomOptional(T value) {
this.value = value;
}
/**
* Returns an {@code CustomOptional} describing the given non-{@code null}
* value.
*
* @param value the value to describe, which must be non-{@code null}
* @param <T> the type of the value
* @return an {@code CustomOptional} with the value present
* @throws NullPointerException if value is {@code null}
*/
public static <T> CustomOptional<T> of(T value) {
return new CustomOptional<>(Objects.requireNonNull(value));
}
/**
* Returns an {@code CustomOptional} describing the given value, if
* non-{@code null}, otherwise returns an empty {@code CustomOptional}.
*
* @param value the possibly-{@code null} value to describe
* @param <T> the type of the value
* @return an {@code CustomOptional} with a present value if the specified value
* is non-{@code null}, otherwise an empty {@code CustomOptional}
*/
@SuppressWarnings("unchecked")
public static <T> CustomOptional<T> ofNullable(T value) {
return value == null ? (CustomOptional<T>) EMPTY
: new CustomOptional<>(value);
}
/**
* If a value is present, returns the value, otherwise throws
* {@code NoSuchElementException}.
*
* @apiNote
* The preferred alternative to this method is {@link #orElseThrow()}.
*
* @return the non-{@code null} value described by this {@code CustomOptional}
* @throws NoSuchElementException if no value is present
*/
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
/**
* If a value is present, returns {@code true}, otherwise {@code false}.
*
* @return {@code true} if a value is present, otherwise {@code false}
*/
public boolean isPresent() {
return value != null;
}
/**
* If a value is not present, returns {@code true}, otherwise
* {@code false}.
*
* @return {@code true} if a value is not present, otherwise {@code false}
* @since 11
*/
public boolean isEmpty() {
return value == null;
}
/**
* If a value is present, performs the given action with the value,
* otherwise does nothing.
*
* @param action the action to be performed, if a value is present
* @throws NullPointerException if value is present and the given action is
* {@code null}
*/
public void ifPresent(Consumer<? super T> action) {
if (value != null) {
action.accept(value);
}
}
/**
* If a value is present, performs the given action with the value,
* otherwise performs the given empty-based action.
*
* @param action the action to be performed, if a value is present
* @param emptyAction the empty-based action to be performed, if no value is
* present
* @throws NullPointerException if a value is present and the given action
* is {@code null}, or no value is present and the given empty-based
* action is {@code null}.
* @since 9
*/
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (value != null) {
action.accept(value);
} else {
emptyAction.run();
}
}
/**
* If a value is present, and the value matches the given predicate,
* returns an {@code CustomOptional} describing the value, otherwise returns an
* empty {@code CustomOptional}.
*
* @param predicate the predicate to apply to a value, if present
* @return an {@code CustomOptional} describing the value of this
* {@code CustomOptional}, if a value is present and the value matches the
* given predicate, otherwise an empty {@code CustomOptional}
* @throws NullPointerException if the predicate is {@code null}
*/
public CustomOptional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent()) {
return this;
} else {
return predicate.test(value) ? this : empty();
}
}
/**
* If a value is present, returns an {@code CustomOptional} describing (as if by
* {@link #ofNullable}) the result of applying the given mapping function to
* the value, otherwise returns an empty {@code CustomOptional}.
*
* <p>If the mapping function returns a {@code null} result then this method
* returns an empty {@code CustomOptional}.
*
* @apiNote
* This method supports post-processing on {@code CustomOptional} values, without
* the need to explicitly check for a return status. For example, the
* following code traverses a stream of URIs, selects one that has not
* yet been processed, and creates a path from that URI, returning
* an {@code CustomOptional<Path>}:
*
* <pre>{@code
* CustomOptional<Path> p =
* uris.stream().filter(uri -> !isProcessedYet(uri))
* .findFirst()
* .map(Paths::get);
* }</pre>
*
* Here, {@code findFirst} returns an {@code CustomOptional<URI>}, and then
* {@code map} returns an {@code CustomOptional<Path>} for the desired
* URI if one exists.
*
* @param mapper the mapping function to apply to a value, if present
* @param <U> The type of the value returned from the mapping function
* @return an {@code CustomOptional} describing the result of applying a mapping
* function to the value of this {@code CustomOptional}, if a value is
* present, otherwise an empty {@code CustomOptional}
* @throws NullPointerException if the mapping function is {@code null}
*/
public <U> CustomOptional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return CustomOptional.ofNullable(mapper.apply(value));
}
}
/**
* If a value is present, returns the result of applying the given
* {@code CustomOptional}-bearing mapping function to the value, otherwise returns
* an empty {@code CustomOptional}.
*
* <p>This method is similar to {@link #map(Function)}, but the mapping
* function is one whose result is already an {@code CustomOptional}, and if
* invoked, {@code flatMap} does not wrap it within an additional
* {@code CustomOptional}.
*
* @param <U> The type of value of the {@code CustomOptional} returned by the
* mapping function
* @param mapper the mapping function to apply to a value, if present
* @return the result of applying an {@code CustomOptional}-bearing mapping
* function to the value of this {@code CustomOptional}, if a value is
* present, otherwise an empty {@code CustomOptional}
* @throws NullPointerException if the mapping function is {@code null} or
* returns a {@code null} result
*/
public <U> CustomOptional<U> flatMap(Function<? super T, ? extends CustomOptional<? extends U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
@SuppressWarnings("unchecked")
CustomOptional<U> r = (CustomOptional<U>) mapper.apply(value);
return Objects.requireNonNull(r);
}
}
/**
* If a value is present, returns an {@code CustomOptional} describing the value,
* otherwise returns an {@code CustomOptional} produced by the supplying function.
*
* @param supplier the supplying function that produces an {@code CustomOptional}
* to be returned
* @return returns an {@code CustomOptional} describing the value of this
* {@code CustomOptional}, if a value is present, otherwise an
* {@code CustomOptional} produced by the supplying function.
* @throws NullPointerException if the supplying function is {@code null} or
* produces a {@code null} result
* @since 9
*/
public CustomOptional<T> or(Supplier<? extends CustomOptional<? extends T>> supplier) {
Objects.requireNonNull(supplier);
if (isPresent()) {
return this;
} else {
@SuppressWarnings("unchecked")
CustomOptional<T> r = (CustomOptional<T>) supplier.get();
return Objects.requireNonNull(r);
}
}
/**
* If a value is present, returns a sequential {@link Stream} containing
* only that value, otherwise returns an empty {@code Stream}.
*
* @apiNote
* This method can be used to transform a {@code Stream} of CustomOptional
* elements to a {@code Stream} of present value elements:
* <pre>{@code
* Stream<CustomOptional<T>> os = ..
* Stream<T> s = os.flatMap(CustomOptional::stream)
* }</pre>
*
* @return the CustomOptional value as a {@code Stream}
* @since 9
*/
public Stream<T> stream() {
if (!isPresent()) {
return Stream.empty();
} else {
return Stream.of(value);
}
}
/**
* If a value is present, returns the value, otherwise returns
* {@code other}.
*
* @param other the value to be returned, if no value is present.
* May be {@code null}.
* @return the value, if present, otherwise {@code other}
*/
public T orElse(T other) {
return value != null ? value : other;
}
/**
* If a value is present, returns the value, otherwise returns the result
* produced by the supplying function.
*
* @param supplier the supplying function that produces a value to be returned
* @return the value, if present, otherwise the result produced by the
* supplying function
* @throws NullPointerException if no value is present and the supplying
* function is {@code null}
*/
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
/**
* If a value is present, returns the value, otherwise throws
* {@code NoSuchElementException}.
*
* @return the non-{@code null} value described by this {@code CustomOptional}
* @throws NoSuchElementException if no value is present
* @since 10
*/
public T orElseThrow() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
/**
* If a value is present, returns the value, otherwise throws an exception
* produced by the exception supplying function.
*
* @apiNote
* A method reference to the exception constructor with an empty argument
* list can be used as the supplier. For example,
* {@code IllegalStateException::new}
*
* @param <X> Type of the exception to be thrown
* @param exceptionSupplier the supplying function that produces an
* exception to be thrown
* @return the value, if present
* @throws X if no value is present
* @throws NullPointerException if no value is present and the exception
* supplying function is {@code null}
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
/**
* Indicates whether some other object is "equal to" this {@code CustomOptional}.
* The other object is considered equal if:
* <ul>
* <li>it is also an {@code CustomOptional} and;
* <li>both instances have no value present or;
* <li>the present values are "equal to" each other via {@code equals()}.
* </ul>
*
* @param obj an object to be tested for equality
* @return {@code true} if the other object is "equal to" this object
* otherwise {@code false}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CustomOptional)) {
return false;
}
CustomOptional<?> other = (CustomOptional<?>) obj;
return Objects.equals(value, other.value);
}
/**
* Returns the hash code of the value, if present, otherwise {@code 0}
* (zero) if no value is present.
*
* @return hash code value of the present value or {@code 0} if no value is
* present
*/
@Override
public int hashCode() {
return Objects.hashCode(value);
}
/**
* Returns a non-empty string representation of this {@code CustomOptional}
* suitable for debugging. The exact presentation format is unspecified and
* may vary between implementations and versions.
*
* @implSpec
* If a value is present the result must include its string representation
* in the result. Empty and present {@code CustomOptional}s must be unambiguously
* differentiable.
*
* @return the string representation of this instance
*/
@Override
public String toString() {
return value != null
? ("Optional[" + value + "]")
: "Optional.empty";
}
}
二、Optional使用的小技巧
我发现一些小伙伴在使用Optional的时候使用错了
本来是避免笨重的if判断 比如说我要获取一个user中的name属性,基于面向最坏编程原则(永远不相信你的数据),这两个都需要判空如果是传统的方法这样写 eg:
User user = userDao.selectOne();
if(user != null){
String userName = user.getUserName();
if(userName != null){
dealWithName(userName);
......
}
}
有些小伙伴用Optional类这样写
User user = userDao.selectOne();
Optional<User> opt = Optional.ofNullable(user);
if(opt.ispresent()){
user = opt.get();
Optional<String> username = Optional.ofNullable(user.getUserName());
if(username.isPresent()){
dealWithName(opt.get());
......
}
}
可是这样写又和if有什么区别呢 , Optional 对 NPE问题(空指针) 的解决主要是它流式写法的妙用
也就是说你想对对象某个属性的判空,你可以从父级一直map()下去,最后整个链路都是空指针安全的
可能光说有点不大理解,下面的例子是正确使用Optional的方式:
User user = userDao.selectOne();
Optional<User> opt = Optional.ofNullable(user);
opt.map(User::getUserName).ifPresent(userName->dealWithName(opt.get()));
更进阶一些可以数据库的返回都是Optional类
Optional<User> opt = userDao.selectOne();
那么上面的代码就变成了
userDao.selectOne().map(User::getUserName).ifPresent(userName->dealWithName(opt.get()));
不仅map还可以加filter 类似list的stream
Objct resilt = userDao.selectOne().map(User::getUserName)
.filter(userName -> "张三".equals(userName ))
.ifPresent(userName->dealWithName(opt.get()));
总结
以上都是纯手打,如果有个别英文错误请见谅.