Java 8(7):自家製多糖switch
背景
JDK 12とJDK 13はすでに発表されており、Java文法の小さな改善を伴っています.例えば、私たちがよく知っている
JDK 12より前
JDK12
JDK13
新しい特性は素晴らしいが、現在業界で最も流行しているバージョンは依然としてJDK 8なので、生産環境でこんなに快適な
インプリメンテーション
JDK 12のswitch
まず、
静的方法
次に、
ちょっと変なの?そうですよ.
まだ何か足りないようですか?そうですよ.
OK、小さなdemoを書いて比較してみましょう.
私たちの
また、カスタム条件文もサポートされているので、
どちらが使いやすく、読みやすいかについては、「仁者見仁、智者見智」です.
JDK 13のスイッチを合わせる
JDK 13には、
消費に使用される
改造が完了し、値を求めるための
次に、条件を満たすときに評価する方法と、いずれかの条件を満たさないときに評価する方法を2つ追加します.
同様に、demoを書いて効果を見てみましょう.
しかし、コンパイルは--戻り値のタイプが導出されないため.......
つまり、私たちが返すタイプを明確にします.
比較するとJDK 13:
明示的に値を返すタイプを除いて、両者の機能は一致している.JDK 13は簡潔ではありませんが、私たちの
demoを書きます:
ここで
最終的な
拡張
in操作
皆さんは何か改善やアイデアがありますか?コメント交流を歓迎します~
JDK 12とJDK 13はすでに発表されており、Java文法の小さな改善を伴っています.例えば、私たちがよく知っている
switch
などです.JDK 12より前
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"
};
新しい特性は素晴らしいが、現在業界で最も流行しているバージョンは依然としてJDK 8なので、生産環境でこんなに快適な
switch
を使いたいのは、まだ遠いようだ.幸いなことに、私たちにはLambdaがあります.「自分で手を出して、衣食を豊かにする」ということです.私たちは自分でJDK12 & JDK13
のswtich
に似たようなものを作って、私たちに平板なコード生活をして、砂糖を加えることができますか.インプリメンテーション
JDK 12のswitch
まず、
Switch
クラスを定義し、Javaのswitch
文と同様に汎用パラメータを受信します.まずパラメータを受信する必要があります.public class Switch {
/**
*
*/
private final T input;
private Switch(T input) {
this.input = input;
}
public static Switch on(T input) {
return new Switch<>(input);
}
}
静的方法
on
(on(input)
はinput上でSwitch
の動作を行うと理解できる)により、Switch
の例を構築することができる.次に、現在の条件を表すPredicate
を定義します.public class Switch {
private Predicate condition;
public Switch is(T target) {
// target
condition = Predicate.isEqual(target);
return this;
}
}
is
メソッドの役割は、現在の条件conditionをSwitchの入力値が入力されたtargetと等しいか否かを判断することである.条件が導入された以上、ユーザー自身で条件を定義することができます.public Switch is(T target) {
// target
return when(Predicate.isEqual(target));
}
public Switch when(Predicate condition) {
//
this.condition = Objects.requireNonNull(condition);
return this;
}
次に、
switch
文のcase ... break
機能を定義します.public Switch thenAccept(Consumer 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
は1つのcase
しか満たしていません.もし私たちが自分でいろいろな条件を設定したら、複数の条件が満たされる可能性があります.それは私たちが予想していたswitch
ではありません.したがって、ユーザが設定した複数の条件にいずれかが満たされているかどうかを示すboolean
タグを定義することができ、1つが満たされている場合、その条件の後のチェーンメソッドは直接短絡処理される.public class Switch {
...
/**
*
*/
private boolean met;
public Switch is(T target) {
return when(Predicate.isEqual(target));
}
public Switch when(Predicate condition) {
//
if (met) { return this; }
this.condition = Objects.requireNonNull(condition);
return this;
}
public Switch thenAccept(Consumer 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 action) {
// ,
if (met) { return; }
Objects.requireNonNull(action);
action.accept(input);
}
OK、小さなdemoを書いて比較してみましょう.
//
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
はJDK 12のswitch
ほど直感的ではないように見えますが、JDK 12以前の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(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");
}
どちらが使いやすく、読みやすいかについては、「仁者見仁、智者見智」です.
JDK 13のスイッチを合わせる
JDK 13には、
switch
文の評価機能が付与されています.私たちのSwitch
を簡単に改造してこの機能をサポートすることもできます.まず、Switch
を抽象化し、ConsumptionSwitch
を消費用のSwitch
(すなわち、上述したSwitch
)とし、EvaluationSwitch
を評価のためのSwitch
として定義する.抽象Switch
:public abstract class Switch {
/**
*
*/
final T input;
/**
*
*/
Predicate condition;
/**
*
*/
boolean met;
Switch(T input) {
this.input = input;
}
/**
* Switch, Switch
*/
public static ConsumptionSwitch on(I input) {
return new ConsumptionSwitch<>(input);
}
/**
* Switch, Switch
*/
public static EvaluationSwitch input(I input) {
return new EvaluationSwitch<>(input);
}
/**
*
*/
protected Switch is(T target) {
return when(Predicate.isEqual(target));
}
/**
*
*/
protected Switch when(Predicate condition) {
//
if (met) { return this; }
this.condition = Objects.requireNonNull(condition);
return this;
}
......
}
消費に使用される
Switch
:/**
* Switch
*
* @param
*/
public static class ConsumptionSwitch extends Switch {
ConsumptionSwitch(T value) {
super(value);
}
@Override
public ConsumptionSwitch is(I target) {
super.is(target);
return this;
}
@Override
public ConsumptionSwitch when(Predicate condition) {
super.when(condition);
return this;
}
/**
* ,
*/
public ConsumptionSwitch thenAccept(Consumer action) {
//
if (met) { return this; }
requireNonNullArgAndCondition(action);
if (condition.test(input)) {
action.accept(input);
//
met = true;
}
return this;
}
/**
* ,
*/
public void elseAccept(Consumer action) {
// ,
if (met) { return; }
Objects.requireNonNull(action);
action.accept(input);
}
}
改造が完了し、値を求めるための
Switch
を実現することができます.まず、汎化タイプの戻り値を定義します./**
* Switch
*
* @param
* @param
*/
public static class EvaluationSwitch extends Switch {
/**
*
*/
private O output;
EvaluationSwitch(I input) {
super(input);
}
@Override
public EvaluationSwitch is(I target) {
super.is(target);
return this;
}
@Override
public EvaluationSwitch when(Predicate condition) {
super.when(condition);
return this;
}
}
次に、条件を満たすときに評価する方法と、いずれかの条件を満たさないときに評価する方法を2つ追加します.
/**
* ,
*/
public EvaluationSwitch 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();
String result = Switch.input(num)
.is(0).thenGet("zero")
.is(1).thenGet("one")
.is(2).thenGet("two")
.elseGet("many");
System.out.println(result);
しかし、コンパイルは--戻り値のタイプが導出されないため.......
Switch.input(k)
はEvaluationSwitch
を返し、私たちが必要とするのはEvaluationSwitch
である.仕方がないので、一つの方法で変換してみましょう./**
* EvaluationSwitch
*
* @param type
* @param
* @return EvaluationSwitch
*/
@SuppressWarnings("unchecked")
public EvaluationSwitch output(Class extends R> type) {
return (EvaluationSwitch) this;
}
つまり、私たちが返すタイプを明確にします.
int num = getNum();
String result = Switch.input(num)
.output(String.class)
.is(0).thenGet("zero")
.is(1).thenGet("one")
.is(2).thenGet("two")
.elseGet("many");
System.out.println(result);
比較するとJDK 13:
int num = getNum();
String value = switch (num) {
case 0 -> "zero"
case 1 -> "one"
case 2 -> "two"
default -> "many"
};
System.out.println(value);
明示的に値を返すタイプを除いて、両者の機能は一致している.JDK 13は簡潔ではありませんが、私たちの
Switch
も非常に直感的に見えます.さらに、Switch
の評価機能をさらに強化するために関数を導入することができます./**
* , Function , Switch Function
*/
public EvaluationSwitch thenApply(Function 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 mapper) {
Objects.requireNonNull(mapper);
return met ? output : mapper.apply(input);
}
/**
* , Supplier
*/
public EvaluationSwitch thenSupply(Supplier supplier) {
if (met) { return this; }
requireNonNullArgAndCondition(supplier);
if (condition.test(input)) {
output = supplier.get();
met = true;
}
return this;
}
/**
* , Supplier
*/
public O elseSupply(Supplier 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拡張
in操作
is
は、入力が特定の値と等しいかどうかを判断するために使用されます.入力が特定の値のグループにあるかどうかを判断する必要がある場合は、次のようにします.when
メソッドに基づいて簡単です./**
*
*
* @param values
* @return Switch
*/
protected Switch in(T... values) {
Objects.requireNonNull(values);
return when(e -> {
for (T value : values) {
if (Objects.equals(e, value)) {
return true;
}
}
return false;
});
}
皆さんは何か改善やアイデアがありますか?コメント交流を歓迎します~