ご存じのように、Operation
enumの古典的な例があります(ただし、Java 8標準インターフェースを使用))。これは次のとおりです。
enum Operation implements DoubleBinaryOperator {
PLUS("+") {
@Override
public double applyAsDouble(final double left, final double right) {
return left + right;
}
},
MINUS("-") {
@Override
public double applyAsDouble(final double left, final double right) {
return left - right;
}
},
MULTIPLY("*") {
@Override
public double applyAsDouble(final double left, final double right) {
return left * right;
}
},
DIVIDE("/") {
@Override
public double applyAsDouble(final double left, final double right) {
return left / right;
}
};
private final String symbol;
private Operation(final String symbol) {
this.symbol = symbol;
}
public String getSymbol() {
return symbol;
}
}
テスト済み:
Arrays.stream(Operation.values())
.forEach(op -> System.out.println("Performing operation " + op.getSymbol() + " on 2 and 4: " + op.applyAsDouble(2, 4)));
それは提供します:
2と4の操作+の実行:6.0
操作の実行-2および4で-2.0
2および4で操作を実行*:8.0
操作の実行/ 2および4で:0.5
しかし、私はJava 8でより良いことができるように感じるので、以下を実装しました:
enum Operation implements DoubleBinaryOperator {
PLUS ("+", (l, r) -> l + r),
MINUS ("-", (l, r) -> l - r),
MULTIPLY("*", (l, r) -> l * r),
DIVIDE ("/", (l, r) -> l / r);
private final String symbol;
private final DoubleBinaryOperator binaryOperator;
private Operation(final String symbol, final DoubleBinaryOperator binaryOperator) {
this.symbol = symbol;
this.binaryOperator = binaryOperator;
}
public String getSymbol() {
return symbol;
}
@Override
public double applyAsDouble(final double left, final double right) {
return binaryOperator.applyAsDouble(left, right);
}
}
機能的には同等ですが、両方の実装は似ていますか、それとも古いバージョンと同じように新しいバージョンを悪化させるいくつかの隠された詳細がありますか?
そして最後に、Java 8)の時点で、ラムダ方式が推奨される方法ですか?
明らかに、ラムダ版ははるかに読みやすくなっています。短いだけでなく、コンストラクターで実装オペレーターを一目で確認できるようになります。 enum
を拡張してint
計算もサポートすることを想像してみてください…
パフォーマンスの観点からは、生成されたラムダクラスによって匿名のenum
内部クラスを交換しています。ラムダバージョンでは、委任のレベルがもう1つ追加されますが、HotSpotオプティマイザーにとってそれは難しいことではありません。実行パフォーマンスに関して違いが見られることはほとんどありません。
ただし、その結果としてラムダパターンを適用すると、クラスを使用するアプリケーションのstartupが高速化される場合があります。その理由は、従来の特殊なenum
アプローチの場合、Javaコンパイラは、ファイルシステムまたは(場合によってはZip圧縮された)いずれかに存在するケースごとに内部クラスを生成する必要があるためです。 Jarファイル内。ラムダクラス(非常に単純な構造を持つ)のバイトコードをオンザフライで生成する方が、通常、クラスをロードするよりも高速です。生成されたラムダクラスのアクセスチェックがないことも役立ちます。
要約すると:
ラムダにとっては大きな勝利です。はい、私はラムダの方法がJava 8。
betterの定義方法によって異なります。
あなたの場合と私の意見では、ラムダは純粋な勝利です。既存のJDK関数の一部を再利用することもできます。例:
enum Operation implements DoubleBinaryOperator {
PLUS ("+", Double::sum),
...
}
これは短くて読みやすいです。コードのベンチマークを行わない場合のパフォーマンスについて、妥当なことは何も言えないと思います。
ラムダはinvokeDynamic
で実装され、呼び出しサイトを実際のコードに動的にリンクして実行します。匿名の内部クラスはありません。
誰もこれについて言及していないことに驚いていますが、ラムダアプローチは、複数のメソッドを実装するときに厄介なものになる可能性があります。名前のないラムダの束をコンストラクタに渡すと、より簡潔になりますが、必ずしも読みやすくなるとは限りません。
また、関数のサイズが大きくなるにつれて、ラムダを使用する利点は減少します。ラムダが数行を超える場合、オーバーライドは読みやすいかもしれませんが、読みやすいかもしれません。
悪いことを定義します。ほとんどの場合、バイトコードが少し多く使用され、少し遅くなります。
これらがあなたにとって重要でない限り、私はあなたがより明確でよりシンプルであると思うアプローチを使用します。