ofBlankAble(T value) {
+ return StrUtil.isBlankIfStr(value) ? empty() : new Opt<>(value);
+ }
+
+ /**
+ * 返回包裹里的元素,取不到则为{@code null},注意!!!此处和{@link java.util.Optional#get()}不同的一点是本方法并不会抛出{@code NoSuchElementException}
+ * 如果元素为空,则返回{@code null},如果需要一个绝对不能为{@code null}的值,则使用{@link #orElseThrow()}
+ *
+ * @return 包裹里的元素,有可能为{@code null}
+ * @apiNote 如果需要一个绝对不能为 {@code null}的值,则使用{@link #orElseThrow()}
+ * 做此处修改的原因是,有时候我们确实需要返回一个null给前端,并且这样的时候并不少见
+ * 而使用 {@code .orElse(null)}需要写整整12个字符,用{@code .get()}就只需要6个啦
+ */
+ public T get() {
+ return value;
+ }
+
+ /**
+ * 判断包裹里元素的值是否存在,存在为 {@code true},否则为{@code false}
+ *
+ * @return 包裹里元素的值存在为 {@code true},否则为{@code false}
+ */
+ public boolean isPresent() {
+ return value != null;
+ }
+
+ /**
+ * 判断包裹里元素的值是否不存在,不存在为 {@code true},否则为{@code false}
+ *
+ * @return 包裹里元素的值不存在 则为 {@code true},否则为{@code false}
+ * @since 11 这是jdk11{@link java.util.Optional}中的新函数
+ */
+ public boolean isEmpty() {
+ return value == null;
+ }
+
+ /**
+ * 如果包裹里的值存在,就执行传入的操作({@link Consumer#accept})
+ *
+ * @param action 你想要执行的操作
+ * @throws NullPointerException 如果包裹里的值存在,但你传入的操作为{@code null}时抛出
+ * @apiNote 例如如果值存在就打印结果
+ * {@code
+ * Opt.ofNullable("Hello Hutool!").ifPresent(Console::log);
+ * }
+ */
+ public void ifPresent(Consumer super T> action) {
+ if (value != null) {
+ action.accept(value);
+ }
+ }
+
+ /**
+ * 如果包裹里的值存在,就执行传入的值存在时的操作({@link Consumer#accept})
+ * 否则执行传入的值不存在时的操作({@link VoidFunc0}中的{@link VoidFunc0#call()})
+ *
+ * @param action 包裹里的值存在时的操作
+ * @param emptyAction 包裹里的值不存在时的操作
+ * @throws NullPointerException 如果包裹里的值存在时,执行的操作为 {@code null}, 或者包裹里的值不存在时的操作为 {@code null},则抛出{@code NPE}
+ * @apiNote 例如值存在就打印对应的值,不存在则用{@code Console.error}打印另一句字符串
+ * {@code
+ * Opt.ofNullable("Hello Hutool!").ifPresentOrElse(Console::log, () -> Console.error("Ops!Something is wrong!"));
+ * }
+ * @since 9 这是jdk9{@link java.util.Optional}中的新函数
+ */
+ public void ifPresentOrElse(Consumer super T> action, VoidFunc0 emptyAction) {
+ if (value != null) {
+ action.accept(value);
+ } else {
+ emptyAction.callWithRuntimeException();
+ }
+ }
+
+ /**
+ * 判断包裹里的值存在并且与给定的条件是否满足 ({@link Predicate#test}执行结果是否为true)
+ * 如果满足条件则返回本身
+ * 不满足条件或者元素本身为空时返回一个返回一个空的{@code Opt}
+ *
+ * @param predicate 给定的条件
+ * @return 如果满足条件则返回本身, 不满足条件或者元素本身为空时返回一个返回一个空的{@code Opt}
+ * @throws NullPointerException 如果给定的条件为 {@code null},抛出{@code NPE}
+ */
+ public Opt filter(Predicate super T> predicate) {
+ Objects.requireNonNull(predicate);
+ if (isEmpty()) {
+ return this;
+ } else {
+ return predicate.test(value) ? this : empty();
+ }
+ }
+
+ /**
+ * 如果包裹里的值存在,就执行传入的操作({@link Function#apply})并返回一个包裹了该操作返回值的{@code Opt}
+ * 如果不存在,返回一个空的{@code Opt}
+ *
+ * @param mapper 值存在时执行的操作
+ * @param 操作返回值的类型
+ * @return 如果包裹里的值存在,就执行传入的操作({@link Function#apply})并返回一个包裹了该操作返回值的{@code Opt},
+ * 如果不存在,返回一个空的{@code Opt}
+ * @throws NullPointerException 如果给定的操作为 {@code null},抛出 {@code NPE}
+ */
+ public Opt map(Function super T, ? extends U> mapper) {
+ Objects.requireNonNull(mapper);
+ if (isEmpty()) {
+ return empty();
+ } else {
+ return Opt.ofNullable(mapper.apply(value));
+ }
+ }
+
+ /**
+ * 如果包裹里的值存在,就执行传入的操作({@link Function#apply})并返回该操作返回值
+ * 如果不存在,返回一个空的{@code Opt}
+ * 和 {@link Opt#map}的区别为 传入的操作返回值必须为 Opt
+ *
+ * @param mapper 值存在时执行的操作
+ * @param 操作返回值的类型
+ * @return 如果包裹里的值存在,就执行传入的操作({@link Function#apply})并返回该操作返回值
+ * 如果不存在,返回一个空的{@code Opt}
+ * @throws NullPointerException 如果给定的操作为 {@code null}或者给定的操作执行结果为 {@code null},抛出 {@code NPE}
+ */
+ public Opt flatMap(Function super T, ? extends Opt extends U>> mapper) {
+ Objects.requireNonNull(mapper);
+ if (isEmpty()) {
+ return empty();
+ } else {
+ @SuppressWarnings("unchecked")
+ Opt r = (Opt) mapper.apply(value);
+ return Objects.requireNonNull(r);
+ }
+ }
+
+ /**
+ * 如果包裹里元素的值存在,就执行对应的操作,并返回本身
+ * 如果不存在,返回一个空的{@code Opt}
+ *
+ * @param action 值存在时执行的操作
+ * @throws NullPointerException 如果值存在,并且传入的操作为 {@code null}
+ * @apiNote 属于 {@link #ifPresent}的链式拓展
+ * @author VampireAchao
+ */
+ public Opt peek(Consumer action) {
+ Objects.requireNonNull(action);
+ if (isEmpty()) {
+ return Opt.empty();
+ }
+ action.accept(value);
+ return this;
+ }
+
+ /**
+ * 如果包裹里元素的值存在,就返回本身,如果不存在,则使用传入的操作执行后获得的 {@code Opt}
+ *
+ * @return 如果包裹里元素的值存在,就返回本身,如果不存在,则使用传入的函数执行后获得的 {@code Opt}
+ * @throws NullPointerException 如果传入的操作为空,或者传入的操作执行后返回值为空,则抛出 {@code NPE}
+ * @since 9 这是jdk9{@link java.util.Optional}中的新函数
+ */
+ public Opt or(Supplier extends Opt extends T>> supplier) {
+ Objects.requireNonNull(supplier);
+ if (isPresent()) {
+ return this;
+ } else {
+ @SuppressWarnings("unchecked")
+ Opt r = (Opt) supplier.get();
+ return Objects.requireNonNull(r);
+ }
+ }
+
+ /**
+ * 如果包裹里元素的值存在,就返回一个包含该元素的 {@link Stream},
+ * 否则返回一个空元素的 {@link Stream}
+ *
+ * @return 返回一个包含该元素的 {@link Stream}或空的 {@link Stream}
+ * @apiNote 该方法能将 Opt 中的元素传递给 {@link Stream}
+ * {@code
+ * Stream> os = ..
+ * Stream s = os.flatMap(Opt::stream)
+ * }
+ * @since 9 这是jdk9{@link java.util.Optional}中的新函数
+ */
+ public Stream stream() {
+ if (isEmpty()) {
+ return Stream.empty();
+ } else {
+ return Stream.of(value);
+ }
+ }
+
+ /**
+ * 如果包裹里元素的值存在,则返回该值,否则返回传入的{@code other}
+ *
+ * @param other 元素为空时返回的值,有可能为 {@code null}.
+ * @return 如果包裹里元素的值存在,则返回该值,否则返回传入的{@code other}
+ */
+ public T orElse(T other) {
+ return value != null ? value : other;
+ }
+
+ /**
+ * 如果包裹里元素的值存在,则返回该值,否则返回传入的操作执行后的返回值
+ *
+ * @param supplier 值不存在时需要执行的操作,返回一个类型与 包裹里元素类型 相同的元素
+ * @return 如果包裹里元素的值存在,则返回该值,否则返回传入的操作执行后的返回值
+ * @throws NullPointerException 如果之不存在,并且传入的操作为空,则抛出 {@code NPE}
+ */
+ public T orElseGet(Supplier extends T> supplier) {
+ return value != null ? value : supplier.get();
+ }
+
+ /**
+ * 如果包裹里的值存在,则返回该值,否则抛出 {@code NoSuchElementException}
+ *
+ * @return 返回一个不为 {@code null} 的包裹里的值
+ * @throws NoSuchElementException 如果包裹里的值不存在则抛出该异常
+ * @since 10 这是jdk10{@link java.util.Optional}中的新函数
+ */
+ public T orElseThrow() {
+ if (value == null) {
+ throw new NoSuchElementException("No value present");
+ }
+ return value;
+ }
+
+ /**
+ * 如果包裹里的值存在,则返回该值,否则执行传入的操作,获取异常类型的返回值并抛出
+ *
+ * @param 异常类型
+ * @param exceptionSupplier 值不存在时执行的操作,返回值继承 {@link Throwable}
+ * @return 包裹里不能为空的值
+ * @throws X 如果值不存在
+ * @throws NullPointerException 如果值不存在并且 传入的操作为 {@code null}或者操作执行后的返回值为{@code null}
+ * @apiNote 往往是一个包含无参构造器的异常 例如传入{@code IllegalStateException::new}
+ */
+ public T orElseThrow(Supplier extends X> exceptionSupplier) throws X {
+ if (value != null) {
+ return value;
+ } else {
+ throw exceptionSupplier.get();
+ }
+ }
+
+ /**
+ * 如果包裹里的值存在,则返回该值,否则执行传入的操作,获取异常类型的返回值并抛出
+ *
+ * @param 异常类型
+ * @param exceptionFunction 值不存在时执行的操作,返回值继承 {@link Throwable}
+ * @param message 作为传入操作执行时的参数,一般作为异常自定义提示语
+ * @return 包裹里不能为空的值
+ * @throws X 如果值不存在
+ * @throws NullPointerException 如果值不存在并且 传入的操作为 {@code null}或者操作执行后的返回值为{@code null}
+ * @apiNote 往往是一个包含 自定义消息 构造器的异常 例如
+ * {@code
+ * Opt.ofNullable(null).orElseThrow(IllegalStateException::new, "Ops!Something is wrong!");
+ * }
+ * @author VampireAchao
+ */
+ public T orElseThrow(Function exceptionFunction, String message) throws X {
+ if (value != null) {
+ return value;
+ } else {
+ throw exceptionFunction.apply(message);
+ }
+ }
+
+ /**
+ * 判断传入参数是否与 {@code Opt}相等
+ * 在以下情况下返回true
+ *
+ * - 它也是一个 {@code Opt} 并且
+ *
- 它们包裹住的元素都为空 或者
+ *
- 它们包裹住的元素之间相互 {@code equals()}
+ *
+ *
+ * @param obj 一个要用来判断是否相等的参数
+ * @return 如果传入的参数也是一个 {@code Opt}并且它们包裹住的元素都为空
+ * 或者它们包裹住的元素之间相互 {@code equals()} 就返回{@code true}
+ * 否则返回 {@code false}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Opt)) {
+ return false;
+ }
+
+ Opt> other = (Opt>) obj;
+ return Objects.equals(value, other.value);
+ }
+
+ /**
+ * 如果包裹内元素为空,则返回0,否则返回元素的 {@code hashcode}
+ *
+ * @return 如果包裹内元素为空,则返回0,否则返回元素的 {@code hashcode}
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
+
+ /**
+ * 返回包裹内元素调用{@code toString()}的结果,不存在则返回{@code null}
+ * return 包裹内元素调用{@code toString()}的结果,不存在则返回{@code null}
+ */
+ @Override
+ public String toString() {
+ return value != null
+ ? value.toString()
+ : null;
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/OptionalBeanTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java
similarity index 55%
rename from hutool-core/src/test/java/cn/hutool/core/bean/OptionalBeanTest.java
rename to hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java
index 367aba5e2..9c52e98fe 100644
--- a/hutool-core/src/test/java/cn/hutool/core/bean/OptionalBeanTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java
@@ -1,22 +1,26 @@
-package cn.hutool.core.bean;
+package cn.hutool.core.lang;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
+import java.util.NoSuchElementException;
+
/**
- * {@link OptionalBean}的单元测试
+ * {@link Opt}的单元测试
*
* @author VampireAchao
*/
-public class OptionalBeanTest {
+public class OptTest {
@Test
public void ofBlankAbleTest() {
// ofBlankAble相对于ofNullable考虑了字符串为空串的情况
- String hutool = OptionalBean.ofBlankAble("").orElse("hutool");
+ String hutool = Opt.ofBlankAble("").orElse("hutool");
Assert.assertEquals("hutool", hutool);
}
@@ -24,7 +28,7 @@ public class OptionalBeanTest {
public void getTest() {
// 和原版Optional有区别的是,get不会抛出NoSuchElementException
// 如果想使用原版Optional中的get这样,获取一个一定不为空的值,则应该使用orElseThrow
- Object opt = OptionalBean.ofNullable(null).get();
+ Object opt = Opt.ofNullable(null).get();
Assert.assertNull(opt);
}
@@ -32,28 +36,30 @@ public class OptionalBeanTest {
public void isEmptyTest() {
// 这是jdk11 Optional中的新函数,直接照搬了过来
// 判断包裹内元素是否为空,注意并没有判断空字符串的情况
- boolean isEmpty = OptionalBean.empty().isEmpty();
+ boolean isEmpty = Opt.empty().isEmpty();
Assert.assertTrue(isEmpty);
}
@Test
+ @Ignore
public void ifPresentOrElseTest() {
// 这是jdk9中的新函数,直接照搬了过来
// 存在就打印对应的值,不存在则用{@code System.err.println}打印另一句字符串
- OptionalBean.ofNullable("Hello Hutool!").ifPresentOrElse(System.out::println, () -> System.err.println("Ops!Something is wrong!"));
- OptionalBean.empty().ifPresentOrElse(System.out::println, () -> System.err.println("Ops!Something is wrong!"));
+ Opt.ofNullable("Hello Hutool!").ifPresentOrElse(Console::log, () -> Console.error("Ops!Something is wrong!"));
+
+ Opt.empty().ifPresentOrElse(Console::log, () -> Console.error("Ops!Something is wrong!"));
}
@Test
public void peekTest() {
User user = new User();
// 相当于ifPresent的链式调用
- OptionalBean.ofNullable("hutool").peek(user::setUsername).peek(user::setNickname);
+ Opt.ofNullable("hutool").peek(user::setUsername).peek(user::setNickname);
Assert.assertEquals("hutool", user.getNickname());
Assert.assertEquals("hutool", user.getUsername());
// 注意,传入的lambda中,对包裹内的元素执行赋值操作并不会影响到原来的元素
- String name = OptionalBean.ofNullable("hutool").peek(username -> username = "123").peek(username -> username = "456").get();
+ String name = Opt.ofNullable("hutool").peek(username -> username = "123").peek(username -> username = "456").get();
Assert.assertEquals("hutool", name);
}
@@ -61,30 +67,42 @@ public class OptionalBeanTest {
public void orTest() {
// 这是jdk9 Optional中的新函数,直接照搬了过来
// 给一个替代的Opt
- String str = OptionalBean.ofNullable(null).or(() -> OptionalBean.ofNullable("Hello hutool!")).map(String::toUpperCase).orElseThrow();
+ String str = Opt.ofNullable(null).or(() -> Opt.ofNullable("Hello hutool!")).map(String::toUpperCase).orElseThrow();
Assert.assertEquals("HELLO HUTOOL!", str);
User user = User.builder().username("hutool").build();
- OptionalBean userOptionalBean = OptionalBean.of(user);
+ Opt userOpt = Opt.of(user);
// 获取昵称,获取不到则获取用户名
- String name = userOptionalBean.map(User::getNickname).or(() -> userOptionalBean.map(User::getUsername)).get();
+ String name = userOpt.map(User::getNickname).or(() -> userOpt.map(User::getUsername)).get();
Assert.assertEquals("hutool", name);
}
- @Test
+ @Test(expected = NoSuchElementException.class)
public void orElseThrowTest() {
// 获取一个不可能为空的值,否则抛出NoSuchElementException异常
- Object obj = OptionalBean.ofNullable(null).orElseThrow();
+ Object obj = Opt.ofNullable(null).orElseThrow();
+ Assert.assertNull(obj);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void orElseThrowTest2() {
// 获取一个不可能为空的值,否则抛出自定义异常
- Object assignException = OptionalBean.ofNullable(null).orElseThrow(IllegalStateException::new);
+ Object assignException = Opt.ofNullable(null).orElseThrow(IllegalStateException::new);
+ Assert.assertNull(assignException);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void orElseThrowTest3() {
// 获取一个不可能为空的值,否则抛出带自定义消息的自定义异常
- Object exceptionWithMessage = OptionalBean.empty().orElseThrow(IllegalStateException::new, "Ops!Something is wrong!");
+ Object exceptionWithMessage = Opt.empty().orElseThrow(IllegalStateException::new, "Ops!Something is wrong!");
+ Assert.assertNull(exceptionWithMessage);
}
@Data
@Builder
@NoArgsConstructor
- private static class User {
+ @AllArgsConstructor
+ static class User {
private String username;
private String nickname;
}