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
类似的东西,好给我们平淡的编码生活,加点糖。
首先,我们定义一个 Switch
类,然后它接收一个泛型参数,就类似与 Java 的 switch
语句,需要接受一个参数。
public class Switch<T> { private final T input; private Switch(T input) { this.input = input; } public static <T> Switch<T> on(T input) { return new Switch<>(input); } }
通过静态方法 on
,我们可以构造出一个 Switch
。然后,我们定义一个 Predicate
,用来表示当前的条件:
public class Switch<T> { private final T input; private Predicate<T> condition; private Switch(T input) { this.input = input; } public static <T> Switch<T> on(T input) { return new Switch<>(input); } public Switch<T> is(T target) { // 判断待检测目标是否和 target 相等 condition = Predicate.isEqual(target); return this; } }
is
方法的作用就是将当前的条件 condition
定义为 判断判断待检测目标是否和传入的 target 相等
。既然都引入条件了,自然我们可以让用户自己来定义条件:
public Switch<T> when(Predicate<T> condition) { Objects.requireNonNull(condition); this.condition = condition; return this; }
接着我们就可以来定义 switch
语句中的 case ... break
功能:
public Switch<T> thenAccept(Consumer<T> action) { Objects.requireNonNull(action); if (condition == null) { throw new IllegalStateException("A condition must be set first."); } if (condition.test(input)) { action.accept(input); } return this; }
好像有点不对劲?对哦, switch
只能满足一个 case
,如果是我们自己来设定条件,可能会存在多个条件都满足的情况 —— 那就不是我们预期的 switch
了。我们可以定义一个 boolean
标记,用来表示用户设定的多个条件是否存在某一个满足,如果有一个满足了,则链式调用后面的消费方法都直接短路处理。
public class Switch<T> { ... /** * 是否已经存在过满足的条件 */ private boolean accepted; public Switch<T> is(T target) { if (!accepted) { // 判断待检测目标是否和 target 相等 condition = Predicate.isEqual(target); } return this; } public Switch<T> when(Predicate<T> condition) { Objects.requireNonNull(condition); if (!accepted) { this.condition = condition; } return this; } public Switch<T> thenAccept(Consumer<T> action) { Objects.requireNonNull(action); if (condition == null) { throw new IllegalStateException("A condition must be set first."); } if (!accepted && condition.test(input)) { action.accept(input); // 标记已经存在过满足的条件 accepted = true; } return this; } }
好像还少了点什么?对哦, switch
还有个 default ... break
。我们定义一个 elseAccept
方法,当且仅当之前没有任何一个条件被满足时,调用这个方法:
public void elseAccept(Consumer<T> action) { Objects.requireNonNull(action); // 之前没有任何一个条件被满足 if (!accepted) { action.accept(input); } }
OK,我们来写个小 demo 对比感受一下:
public class Demo { public static void main(String[] args) { // 获得前端传递的某个参数 String type = getType(); 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.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
,支持任何类型,比如:
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(v -> v instanceof Integer) .thenAccept(v -> System.out.println("value is Integer")) .when(v -> v instanceof String) .thenAccept(v -> System.out.println("value is String")) .when(v -> v instanceof Boolean) .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
来支持这个功能。首先,加入返回值的泛化类型:
public class Switch<T, R> { private final T input; private R output; /** * 是否已经存在输出 */ private boolean outputted; }
然后加入两个方法,用来满足条件时求值和不满足任意条件时求值:
public Switch<T, R> thenGet(R value) { if (condition == null) { throw new IllegalStateException("A condition must be set first."); } // 满足条件 if (!outputted && condition.test(input)) { output = value; // 标记已经存在输出 outputted = true; } return this; } public R elseGet(R value) { return outputted ? output : value; }
同样,写个 demo 看看效果:
int k = getNum(); String value = Switch.on(k) .is(0).thenGet("zero") .is(1).thenGet("one") .is(2).thenGet("two") .elseGet("many"); System.out.println(value);
然而,编译不过 —— 因为 Java 推导不出返回值的类型 —— 哎,Java 的泛型啊... 没办法,加入点东西 “示意” 一下吧:
public static <T, R> Switch<T, R> on(T input, Class<R> outType) { return new Switch<>(input); }
即告诉 Java 编译器我们要返回的类型:
int num = getNum(); String value = Switch.on(num, String.class) .is(0).thenGet("zero") .is(1).thenGet("one") .is(2).thenGet("two") .elseGet("many"); System.out.println(value);
对比下 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
的求值功能:
public Switch<T, R> thenApply(Function<T, R> mapper) { Objects.requireNonNull(mapper); if (condition == null) { throw new IllegalStateException("A condition must be set first."); } if (!outputted && condition.test(input)) { output = mapper.apply(input); // 标记已经存在输出 outputted = true; } return this; }
写个 demo:
long getLongValueWithIfElse(Object value) { if (value instanceof Long) { return ((Long) value); } if (value instanceof Integer) { return ((Integer) value).longValue(); } if (value instanceof String) { return Long.parseLong((String) value); } if (value instanceof BigInteger) { return ((BigInteger) value).longValue(); } if (value instanceof BigDecimal) { return ((BigDecimal) value).longValue(); } return Long.MIN_VALUE; } long getLongValueWithSwitch(Object value) { return Switch.on(value, Long.class) .when(Long.class::isInstance) .thenApply(Long.class::cast) .when(Integer.class::isInstance) .thenApply(v -> ((Integer) v).longValue()) .when(String.class::isInstance) .thenApply(v -> Long.parseLong((String) v)) .when(BigInteger.class::isInstance) .thenApply(v -> ((BigInteger) v).longValue()) .when(BigDecimal.class::isInstance) .thenApply(v -> ((BigDecimal) v).longValue()) .elseGet(Long.MAX_VALUE); }
大家还有什么改进和想法呢,欢迎评论交流~