Function.identity()
メソッドの使い方について質問があります。
次のようなコードを想像してみてください。
Arrays.asList("a", "b", "c")
.stream()
.map(Function.identity()) // <- This,
.map(str -> str) // <- is the same as this.
.collect(Collectors.toMap(
Function.identity(), // <-- And this,
str -> str)); // <-- is the same as this.
str->str
の代わりにFunction.identity()
を使用する(またはその逆)理由があるのでしょうか。私は第二の選択肢が読みやすいと思います(もちろん好みの問題)。しかし、なぜそれが好まれるべきなのか "本当の"理由はありますか?
現在のJRE実装では、Function.identity()
は常に同じインスタンスを返しますが、identifier -> identifier
が出現するたびに独自のインスタンスが作成されるだけでなく、異なる実装クラスも作成されます。詳しくは、 ここ を参照してください。
その理由は、コンパイラーがそのラムダ式の自明な本体(x->x
の場合、return identifier;
と同等)を保持する合成メソッドを生成し、このメソッドを呼び出す機能インターフェースの実装を作成するようにランタイムに指示するためです。そのため、ランタイムは異なるターゲットメソッドのみを認識し、現在の実装はメソッドを分析して特定のメソッドが同等かどうかを判断しません。
そのため、x -> x
の代わりにFunction.identity()
を使用するとメモリを節約することができますが、x -> x
の方がFunction.identity()
より読みやすいと判断した場合、それで決定を下すことはできません。
デバッグ情報を有効にしてコンパイルした場合、合成メソッドはラムダ式を保持するソースコード行を指すline debug属性を持つことになります。したがって、特定のFunction
インスタンスのソースを見つける可能性があります。デバッグ対照的に、操作のデバッグ中にFunction.identity()
によって返されたインスタンスに遭遇した場合、誰がそのメソッドを呼び出してインスタンスを操作に渡したのかわかりません。
あなたの例ではstr -> str
とFunction.identity()
の間に大きな違いはありません。なぜなら内部的にそれは単にt->t
だからです。
しかし、Function
を使用できないため、Function.identity
を使用できないことがあります。ここで見てみましょう:
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
これはうまくコンパイルされます
int[] arrayOK = list.stream().mapToInt(i -> i).toArray();
あなたがコンパイルしようとした場合しかし
int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();
mapToInt
はToIntFunction
を必要とするため、コンパイルエラーになります。これはFunction
とは関係ありません。またToIntFunction
はidentity()
メソッドを持っていません。