From 600f2ff371d0935a0d1e4716026833aa3619b53d Mon Sep 17 00:00:00 2001 From: VampireAchao Date: Mon, 15 Nov 2021 23:09:03 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=8B=93=E5=B1=95ofEmptyAble=E5=87=BD?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E7=AE=80=E5=8C=96=E4=BA=86=E5=88=A4=E7=A9=BA?= =?UTF-8?q?=E5=85=83=E7=B4=A0list=E7=9A=84=E6=93=8D=E4=BD=9C=202.ifPresent?= =?UTF-8?q?OrElse=E7=8E=B0=E5=9C=A8=E6=94=AF=E6=8C=81=E9=93=BE=E5=BC=8F?= =?UTF-8?q?=E8=B0=83=E7=94=A8=203.=E6=8B=93=E5=B1=95mapOrElse=E5=87=BD?= =?UTF-8?q?=E6=95=B0,=E5=8F=AF=E7=94=A8=E4=BA=8E=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E8=BF=9E=E7=BB=AD=E7=9A=84=E5=8F=82=E6=95=B0=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=204.=E6=8B=93=E5=B1=95exec=E5=87=BD=E6=95=B0=EF=BC=8C=E8=83=BD?= =?UTF-8?q?=E5=AF=B9NPE=E5=92=8C=E6=95=B0=E7=BB=84=E8=B6=8A=E7=95=8C?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E8=BF=9B=E8=A1=8C=E5=8F=8B=E5=A5=BD=E8=BF=94?= =?UTF-8?q?=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/hutool/core/lang/Opt.java | 59 ++++++++++++++++++- .../java/cn/hutool/core/lang/OptTest.java | 48 ++++++++++++++- 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java index e4bfe9e80..9d12052de 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java @@ -24,9 +24,11 @@ */ package cn.hutool.core.lang; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.func.VoidFunc0; import cn.hutool.core.util.StrUtil; +import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; @@ -97,6 +99,17 @@ public class Opt { return StrUtil.isBlankIfStr(value) ? empty() : new Opt<>(value); } + /** + * 返回一个包裹里{@code List}集合可能为空的{@code Opt},额外判断了集合内元素为空的情况 + * + * @param value 传入需要包裹的元素 + * @param 包裹里元素的类型 + * @return 一个包裹里元素可能为空的 {@code Opt} + */ + public static Opt> ofEmptyAble(List value) { + return CollectionUtil.isEmpty(value) ? empty() : new Opt<>(value); + } + /** * 包裹里实际的元素 */ @@ -176,11 +189,37 @@ public class Opt { * @param emptyAction 包裹里的值不存在时的操作 * @throws NullPointerException 如果包裹里的值存在时,执行的操作为 {@code null}, 或者包裹里的值不存在时的操作为 {@code null},则抛出{@code NPE} */ - public void ifPresentOrElse(Consumer action, VoidFunc0 emptyAction) { - if (value != null) { + public Opt ifPresentOrElse(Consumer action, VoidFunc0 emptyAction) { + if (isPresent()) { action.accept(value); + return ofNullable(value); } else { emptyAction.callWithRuntimeException(); + return empty(); + } + } + + + /** + * 如果包裹里的值存在,就执行传入的值存在时的操作({@link Function#apply(Object)})支持链式调用、转换为其他类型 + * 否则执行传入的值不存在时的操作({@link VoidFunc0}中的{@link VoidFunc0#call()}) + * + *

+ * 如果值存在就转换为大写,否则用{@code Console.error}打印另一句字符串 + *

{@code
+	 * String hutool = Opt.ofBlankAble("hutool").mapOrElse(String::toUpperCase, () -> Console.log("yes")).mapOrElse(String::intern, () -> Console.log("Value is not present~")).get();
+	 * }
+ * + * @param mapper 包裹里的值存在时的操作 + * @param emptyAction 包裹里的值不存在时的操作 + * @throws NullPointerException 如果包裹里的值存在时,执行的操作为 {@code null}, 或者包裹里的值不存在时的操作为 {@code null},则抛出{@code NPE} + */ + public Opt mapOrElse(Function mapper, VoidFunc0 emptyAction) { + if (isPresent()) { + return ofNullable(mapper.apply(value)); + } else { + emptyAction.callWithRuntimeException(); + return empty(); } } @@ -427,6 +466,22 @@ public class Opt { return Optional.ofNullable(this.value); } + + /** + * 执行一系列操作,如果途中发生 {@code NPE} 和 {@code IndexOutOfBoundsException},返回一个空的{@code Opt} + * + * @param supplier 操作 + * @param 类型 + * @return 操作执行后的值 + */ + public static Opt exec(Supplier supplier) { + try { + return Opt.ofNullable(supplier.get()); + } catch (NullPointerException | IndexOutOfBoundsException e) { + return empty(); + } + } + /** * 判断传入参数是否与 {@code Opt}相等 * 在以下情况下返回true diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java index 0da48a24a..5409afbb2 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java @@ -1,5 +1,6 @@ package cn.hutool.core.lang; +import cn.hutool.core.collection.CollectionUtil; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -9,6 +10,7 @@ import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Stream; @@ -46,11 +48,14 @@ public class OptTest { @Test @Ignore public void ifPresentOrElseTest() { - // 这是jdk9中的新函数,直接照搬了过来 // 存在就打印对应的值,不存在则用{@code System.err.println}打印另一句字符串 Opt.ofNullable("Hello Hutool!").ifPresentOrElse(Console::log, () -> Console.error("Ops!Something is wrong!")); Opt.empty().ifPresentOrElse(Console::log, () -> Console.error("Ops!Something is wrong!")); + + // 拓展为支持链式调用 + Opt.empty().ifPresentOrElse(Console::log, () -> Console.error("Ops!Something is wrong!")) + .ifPresentOrElse(Console::log, () -> Console.error("Ops!Something is wrong!")); } @Test @@ -142,6 +147,47 @@ public class OptTest { Assert.assertNull(user.getNickname()); } + @Test + public void ofEmptyAbleTest() { + // 以前,输入一个CollectionUtil感觉要命,类似前缀的类一大堆,代码补全形同虚设(在项目中起码要输入完CollectionU才能在第一个调出这个函数) + // 关键它还很常用,判空和判空集合真的太常用了... + List past = Opt.ofNullable(Collections.emptyList()).filter(CollectionUtil::isNotEmpty).orElseGet(() -> Collections.singletonList("hutool")); + // 现在,一个ofEmptyAble搞定 + List hutool = Opt.ofEmptyAble(Collections.emptyList()).orElseGet(() -> Collections.singletonList("hutool")); + Assert.assertEquals(past, hutool); + Assert.assertEquals(hutool, Collections.singletonList("hutool")); + } + + @Test + public void mapOrElseTest() { + // 如果值存在就转换为大写,否则打印一句字符串,支持链式调用、转换为其他类型 + String hutool = Opt.ofBlankAble("hutool").mapOrElse(String::toUpperCase, () -> Console.log("yes")).mapOrElse(String::intern, () -> Console.log("Value is not present~")).get(); + Assert.assertEquals("HUTOOL", hutool); + } + + @Test + public void execTest() { + // 有一些资深的程序员跟我说你这个lambda,双冒号语法糖看不懂... + // 为了尊重资深程序员的意见,并且提升代码可读性,封装了一下 "try catch NPE 和 数组越界"的情况 + + // 以前这种写法,简洁但可读性稍低,对资深程序员不太友好 + List last = null; + String npeSituation = Opt.ofEmptyAble(last).flattedMap(l -> l.stream().findFirst()).orElse("hutool"); + String indexOutSituation = Opt.ofEmptyAble(last).map(l -> l.get(0)).orElse("hutool"); + + // 现在代码整洁度降低,但可读性up,如果再人说看不懂这代码... + String npe = Opt.exec(() -> last.get(0)).orElse("hutool"); + String indexOut = Opt.exec(() -> { + List list = new ArrayList<>(); + // 你可以在里面写一长串调用链 list.get(0).getUser().getId() + return list.get(0); + }).orElse("hutool"); + Assert.assertEquals(npe, npeSituation); + Assert.assertEquals(indexOut, indexOutSituation); + Assert.assertEquals("hutool", npe); + Assert.assertEquals("hutool", indexOut); + } + @Data @Builder @NoArgsConstructor