背景

JDK 12 和 JDK 13 已经发布了,伴随着许多对 Java 语法的小改进,比如我们非常熟悉的 switch

JDK12 之前

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;}

JDK12

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'错误,请检查");}

JDK13

String value = switch (i) {    case  0 -> "zero"    case  1 -> "one"    case  2 -> "two"    default -> "many"};

新特性很美好,但是如今在业界最流行的版本依然是 JDK8,所以想要在生产环境用上这么舒服的 switch,目前看来还是遥遥无期。好在我们还有 Lambda,正所谓 “自己动手,丰衣足食”,我们来试试能不能自己做出一个和 JDK12 & JDK13swtich 类似的东西,好给我们平淡的编码生活,加点糖。

实现

对标 JDK12 的 switch

首先,我们定义一个 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-elseif (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

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);}

扩展

大家还有什么改进和想法呢,欢迎评论交流~