JDK 12 和 JDK 13 已经发布了,伴随着许多对 Java 语法的小改进,好比咱们很是熟悉的 switch
:前端
switch (type) { case "all": System.out.println("列出全部帖子"); break; case "auditing": System.out.println("列出审核中的帖子"); break; case "accepted": System.out.println("列出审核经过的帖子"); break; case "rejected": System.out.println("列出审核不经过的帖子"); break; default: System.out.println("参数'type'错误,请检查"); break; }
switch (type) { case "all" -> System.out.println("列出全部帖子"); case "auditing" -> System.out.println("列出审核中的帖子"); case "accepted" -> System.out.println("列出审核经过的帖子"); case "rejected" -> System.out.println("列出审核不经过的帖子"); default -> System.out.println("参数'type'错误,请检查"); }
String value = switch (i) { case 0 -> "zero" case 1 -> "one" case 2 -> "two" default -> "many" };
新特性很美好,可是现在在业界最流行的版本依然是 JDK8,因此想要在生产环境用上这么舒服的 switch
,目前看来仍是遥遥无期。好在咱们还有 Lambda,正所谓 “本身动手,丰衣足食”,咱们来试试能不能本身作出一个和 JDK12 & JDK13
的 swtich
相似的东西,给咱们平淡的编码生活,加点糖。java
首先,咱们定义一个 Switch
类,而后它接收一个泛型参数,就相似与 Java 的 switch
语句,须要先接收一个参数。git
public class Switch<T> { /** * 输入值 */ private final T input; private Switch(T input) { this.input = input; } public static <T, R> Switch<T, R> on(T value) { return new Switch<>(value); } }
经过静态方法 on
(on(input)
能够理解为在 value 上作 Switch
操做),咱们能够构造出一个 Switch
实例。而后,咱们定义一个 Predicate
,用来表示当前的条件:github
public class Switch<T> { private Predicate<T> condition; public Switch<T> is(T target) { // 判断输入值是否和 target 相等 condition = Predicate.isEqual(target); return this; } }
is
方法的做用就是将当前的条件 condition 定义为 判断 Switch 的输入值是否和传入的 target 相等。既然都引入条件了,天然咱们可让用户本身来定义条件:json
public Switch<T, R> is(T target) { // 判断输入值是否和 target 相等 return when(Predicate.isEqual(target)); } public Switch<T, R> when(Predicate<T> condition) { // 用户本身设定条件 this.condition = Objects.requireNonNull(condition); return this; }
接着咱们就能够来定义 switch
语句中的 case ... break
功能:app
public Switch<T> thenAccept(Consumer<T> action) { requireNonNullArgAndCondition(action); if (condition.test(input)) { action.accept(input); } return this; } void requireNonNullCondition() { if (condition == null) { throw new IllegalStateException("A condition must be set first"); } } void requireNonNullArgAndCondition(Object arg) { Objects.requireNonNull(arg, "Null argument " + arg.getClass().getName()); requireNonNullCondition(); }
好像有点不对劲?对哦,switch
只能知足一个 case
,若是是咱们本身来设定各类条件,可能会存在多个条件都知足的状况 —— 那就不是咱们预期的 switch
了。因此咱们能够定义一个 boolean
标记,用来表示用户设定的多个条件是否存在某一个知足,若是有一个知足了,则该条件以后的链式方法都直接 短路处理。ide
public class Switch<T> { ... /** * 是否已经存在过知足的条件 */ private boolean met; public Switch<T, R> is(T target) { return when(Predicate.isEqual(target)); } public Switch<T, R> when(Predicate<T> condition) { // 短路处理 if (met) { return this; } this.condition = Objects.requireNonNull(condition); return this; } public Switch<T, R> thenAccept(Consumer<T> action) { // 短路处理 if (met) { return this; } requireNonNullArgAndCondition(action); if (condition.test(input)) { action.accept(input); // 标记已经存在过知足的条件 met = true; } return this; } }
好像还少了点什么?对哦,switch
还有个 default ... break
。那咱们定义一个 elseAccept
方法,当且仅当以前没有任何一个条件被知足时,调用这个方法:函数
public void elseAccept(Consumer<T> action) { // 以前存在被知足的条件,直接返回 if (met) { return; } Objects.requireNonNull(action); action.accept(input); }
OK,咱们来写个小 demo 对比感觉一下:ui
// 得到前端传递的某个参数 String type = getType(); // 传统 switch switch (type) { case "all": System.out.println("列出全部帖子"); break; case "auditing": System.out.println("列出审核中的帖子"); break; case "accepted": System.out.println("列出审核经过的帖子"); break; case "rejected": System.out.println("列出审核不经过的帖子"); break; default: System.out.println("参数'type'错误,请检查"); break; } // 咱们的 Switch Switch.on(type) .is("all") .thenAccept(t -> System.out.println("列出全部帖子")) .is("auditing") .thenAccept(t -> System.out.println("列出审核中的帖子")) .is("accepted") .thenAccept(t -> System.out.println("列出审核经过的帖子")) .is("rejected") .thenAccept(t -> System.out.println("列出审核不经过的帖子")) .elseAccept(t -> System.out.println("参数'type'错误,请检查"));
虽然咱们的 Switch
看起来没有 JDK12 的 switch
那样直观,可是比 JDK12 以前的 switch
语句更加简洁了 —— 并且链式调用,配合 Lambda,写起来更舒服了~ 更重要的是,咱们都知道 switch
语句支持的类型有限(整数、枚举、字符,字符串),而咱们自定义的 Switch
,支持任何类型,好比:this
Object value = getValue(); Switch.on(value) .is(null) .thenAccept(v -> System.out.println("value is null")) .is(123) .thenAccept(v -> System.out.println("value is 123")) .is("abc") .thenAccept(v -> System.out.println("value is abc")) .is(Arrays.asList(1, 2, 3)) .thenAccept(v -> System.out.println("value is [1, 2, 3]")) .elseAccept(v -> System.out.println("Unknown value"));
并且咱们还支持自定义条件语句,因此很显然,咱们的 Switch
能够用来代替 if-else
语句:
Object value = getValue(); Switch.on(value) .is(null) .thenAccept(v -> System.out.println("value is null")) .when(Integer.class::isInstance) .thenAccept(v -> System.out.println("value is Integer")) .when(String.class::isInstance) .thenAccept(v -> System.out.println("value is String")) .when(Boolean.class::isInstance) .thenAccept(v -> System.out.println("value is Boolean")) .elseAccept(v -> System.out.println("Unknown type of value")); // 等价的 if-else if (value == null) { System.out.println("value is null"); } else if (value instanceof Integer) { System.out.println("value is Integer"); } else if (value instanceof String) { System.out.println("value is String"); } else if (value instanceof Boolean) { System.out.println("value is Boolean"); } else { System.out.println("Unknown type of value"); }
至于哪一个更好用和阅读起来更舒服,就 “仁者见仁,智者见智” 了。
JDK13 中,赋予了 switch
语句求值的功能 —— 咱们也能够很容易的改造咱们的 Switch
来支持这个功能。首先,咱们对 Switch
进行抽象,并定义 ConsumptionSwitch
做为消费用的 Switch
(即上文中实现的 Switch
),定义 EvaluationSwitch
做为用于求值的 Switch
。 抽象 Switch
:
public abstract class Switch<T> { /** * 输入值 */ final T input; /** * 当前的条件 */ Predicate<T> condition; /** * 是否已经存在某个条件被知足 */ boolean met; Switch(T input) { this.input = input; } /** * 在指定的值上使用 Switch,返回用于消费的 Switch 实例 */ public static <I> ConsumptionSwitch<I> on(I input) { return new ConsumptionSwitch<>(input); } /** * 在指定的输入值上使用 Switch,返回用于求值的 Switch 实例 */ public static <I, O> EvaluationSwitch<I, O> in(I input) { return new EvaluationSwitch<>(input); } /** * 判断输入是否和给定的目标相等 */ protected Switch<T> is(T target) { return when(Predicate.isEqual(target)); } /** * 设定输入值须要知足的条件 */ protected Switch<T> when(Predicate<T> condition) { // 短路处理 if (met) { return this; } this.condition = Objects.requireNonNull(condition); return this; } ...... }
用于消费的的 Switch
:
/** * 用于消费的 Switch * * @param <T> 输入值的类型 */ public static class ConsumptionSwitch<V> extends Switch<V> { ConsumptionSwitch(V value) { super(value); } @Override public ConsumptionSwitch<V> is(V target) { super.is(target); return this; } @Override public ConsumptionSwitch<V> when(Predicate<V> condition) { super.when(condition); return this; } /** * 知足某个条件时,对输入值进行消费操做 */ public ConsumptionSwitch<V> thenAccept(Consumer<V> action) { // 短路处理 if (met) { return this; } requireNonNullArgAndCondition(action); if (condition.test(input)) { action.accept(input); // 标记已经存在过知足的条件 met = true; } return this; } /** * 不知足任一条件时,对输入值进行消费操做 */ public void elseAccept(Consumer<V> action) { // 以前存在被知足的条件,直接返回 if (met) { return; } Objects.requireNonNull(action); action.accept(input); } }
改造完毕,如今咱们能够来实现用于求值的 Switch
。首先,定义一个泛化类型的返回值:
/** * 用于求值的 Switch * * @param <I> 输入值的类型 * @param <O> 输出值的类型 */ public static class EvaluationSwitch<I, O> extends Switch<I> { /** * 输出 */ private O output; EvaluationSwitch(I input) { super(input); } @Override public EvaluationSwitch<I, O> is(I target) { super.is(target); return this; } @Override public EvaluationSwitch<I, O> when(Predicate<I> condition) { super.when(condition); return this; } }
而后加入两个方法,用来知足条件时进行求值和不知足任一条件时求值:
/** * 知足某个条件时,进行求值操做 */ public EvaluationSwitch<I, O> thenGet(O value) { if (met) { return this; } requireNonNullCondition(); // 知足条件 if (condition.test(input)) { output = value; // 标记已经产生输出值 met = true; } return this; } /** * 不知足任一条件时,进行求值操做 */ public O elseGet(O value) { return met ? output : value; }
一样,写个 demo 看看效果:
int num = getNum(); // 输入 num 进行求值 String result = Switch.in(num) .is(0).thenGet("zero") .is(1).thenGet("one") .is(2).thenGet("two") .elseGet("many"); System.out.println(result);
然而,编译不过 —— 由于推导不出返回值的类型....... Switch.input(k)
返回的是 EvaluationSwitch<Integer, Object>
,而咱们须要的是 EvaluationSwitch<Integer, String>
。没办法,经过一个方法转换一下吧:
/** * 设定当前 EvaluationSwitch 的输出值的类型 * * @param type 输出值的类型 * @param <R> 指定的输出值类型 * @return 当前的 EvaluationSwitch 实例 */ @SuppressWarnings("unchecked") public <R> EvaluationSwitch<I, R> out(Class<? extends R> type) { return (EvaluationSwitch<I, R>) this; }
即明确咱们要返回的类型:
int num = getNum(); String result = Switch.in(num) .out(String.class) .is(0).thenGet("zero") .is(1).thenGet("one") .is(2).thenGet("two") .elseGet("many"); System.out.println(result);
对比下 JDK13:
int num = getNum(); String value = switch (num) { case 0 -> "zero" case 1 -> "one" case 2 -> "two" default -> "many" }; System.out.println(value);
除了要明确下返回值的类型,两者功能一致;虽然没有 JDK13 简洁,可是咱们的 Switch
看起来也很是的直观。并且咱们能够引入函数来进一步加强咱们的 Switch
的求值功能:
/** * 知足某个条件时,使用 Function 进行求值操做,当前 Switch 实例的输入值会做为 Function 的输入 */ public EvaluationSwitch<I, O> thenApply(Function<I, O> mapper) { if (met) { return this; } requireNonNullArgAndCondition(mapper); if (condition.test(input)) { output = mapper.apply(input); met = true; } return this; } /** * 不知足任一条件时,使用 Function 进行求值操做,当前 Switch 实例的输入值会做为 Function 的输入 */ public O elseApply(Function<I, O> mapper) { Objects.requireNonNull(mapper); return met ? output : mapper.apply(input); } /** * 知足某个条件时,使用 Supplier 进行求值操做 */ public EvaluationSwitch<I, O> thenSupply(Supplier<O> supplier) { if (met) { return this; } requireNonNullArgAndCondition(supplier); if (condition.test(input)) { output = supplier.get(); met = true; } return this; } /** * 不知足任一条件时,使用 Supplier 进行求值操做 */ public O elseSupply(Supplier<O> supplier) { Objects.requireNonNull(supplier); return met ? output : supplier.get(); }
写个 demo:
ScheduleTypeEnum scheduleType = getScheduleType(); // 使用 if-else LocalDateTime ptTime; if (scheduleType == BY_DAY) { ptTime = LocalDateTime.now().minusDays(1); } else if (scheduleType == BY_HOUR) { ptTime = LocalDateTime.now().minusHours(1); } else if (scheduleType == BY_MINUTE) { ptTime = LocalDateTime.now().minusMinutes(1); } else { ptTime = LocalDateTime.now().minusSeconds(1); } // 使用 Java8 switch LocalDateTime ptTime; switch (scheduleType) { case BY_DAY: ptTime = LocalDateTime.now().minusDays(1); break; case BY_HOUR: ptTime = LocalDateTime.now().minusHours(1); break; case BY_MINUTE: ptTime = LocalDateTime.now().minusMinutes(1); break; default: ptTime = LocalDateTime.now().minusMinutes(1); break; } // 使用本文的求值 Switch LocalDateTime ptTime = Switch.input(scheduleType) .output(LocalDateTime.class) .is(BY_DAY) .thenSupply(() -> LocalDateTime.now().minusDays(1)) .is(BY_HOUR) .thenSupply(() -> LocalDateTime.now().minusHours(1)) .is(BY_MINUTE) .thenSupply(() -> LocalDateTime.now().minusMinutes(1)) .elseSupply(() -> LocalDateTime.now().minusSeconds(1));
之因此这里使用 thenSupply
而不是直接使用 thenGet
,是由于使用函数能够 惰性求值。
最终的 Switch
代码可见:Switch.java
is
用来判断 输入 是否和某个 特定值 相等,那若是须要判断 输入 是否在某个 一群值 中呢?很简单,一样基于 when
方法:
/** * 判断输入是否存在给定的一群值中 * * @param values 给定的一群值 * @return 当前 Switch 实例 */ protected Switch<T> isIn(T... values) { Objects.requireNonNull(values); return when(e -> { for (T value : values) { if (Objects.equals(e, value)) { return true; } } return false; }); }
你们还有什么改进和想法呢,欢迎评论交流~