[解決済み] ラムダを比較する方法はありますか?
質問
ラムダ式(クロージャ)を使って定義されたオブジェクトのリストがあるとします。 それらを比較できるように検査する方法はありますか?
私が最も興味を持っているコードは
List<Strategy> strategies = getStrategies();
Strategy a = (Strategy) this::a;
if (strategies.contains(a)) { // ...
完全なコードは
import java.util.Arrays;
import java.util.List;
public class ClosureEqualsMain {
interface Strategy {
void invoke(/*args*/);
default boolean equals(Object o) { // doesn't compile
return Closures.equals(this, o);
}
}
public void a() { }
public void b() { }
public void c() { }
public List<Strategy> getStrategies() {
return Arrays.asList(this::a, this::b, this::c);
}
private void testStrategies() {
List<Strategy> strategies = getStrategies();
System.out.println(strategies);
Strategy a = (Strategy) this::a;
// prints false
System.out.println("strategies.contains(this::a) is " + strategies.contains(a));
}
public static void main(String... ignored) {
new ClosureEqualsMain().testStrategies();
}
enum Closures {;
public static <Closure> boolean equals(Closure c1, Closure c2) {
// This doesn't compare the contents
// like others immutables e.g. String
return c1.equals(c2);
}
public static <Closure> int hashCode(Closure c) {
return // a hashCode which can detect duplicates for a Set<Strategy>
}
public static <Closure> String asString(Closure c) {
return // something better than Object.toString();
}
}
public String toString() {
return "my-ClosureEqualsMain";
}
}
各ラムダをフィールドとして定義し、そのフィールドのみを使用することが唯一の解決策であると思われます。もし、呼び出されたメソッドを出力したい場合は
Method
. ラムダ式でより良い方法はありますか?
また、ラムダを印刷して、人間が読めるものを得ることは可能でしょうか? もしあなたが
this::a
の代わりに
ClosureEqualsMain$$Lambda$1/821270929@3f99bd52
のようなものを取得します。
ClosureEqualsMain.a()
を使うか、あるいは
this.toString
とメソッドで指定します。
my-ClosureEqualsMain.a();
どのように解決するのですか?
この質問は、仕様や実装に関連して解釈される可能性があります。 明らかに実装は変わる可能性がありますが、そうなったときにコードを書き換えることを望んでいるかもしれないので、両方で回答することにします。
また、あなたが何をしたいのかにもよります。 最適化したいのか、それとも 2 つのインスタンスが同じ関数である (またはそうでない) という鉄壁の保証を求めているのか。 (後者の場合、2つの関数が同じものを計算するかどうかというような単純な問題でさえ、決定不可能であるという点で、計算物理学と対立していることに気づくでしょう)。
仕様の観点から、言語仕様はラムダ式の評価(呼び出しではない)の結果がターゲット関数インターフェースを実装するクラスのインスタンスであることだけを約束しています。 結果の同一性、またはエイリアシングの程度については何も約束されていません。 これは、実装に最大限の柔軟性を持たせ、より良いパフォーマンスを提供するための設計です (これがラムダが内部クラスよりも高速である理由です。内部クラスのように、quot; must create unique instance" という制約に縛られません。) 。
つまり、基本的には、参照等しい(==)2つのラムダが同じ関数を計算することを除いて、仕様はあまり教えてくれません。
実装の観点からは、もう少し結論を出すことができます。 ラムダを実装する合成クラスと、プログラム内のキャプチャサイトの間には、1対1の関係があります(現在、変更される可能性があります)。 つまり、"x -> x + 1"をキャプチャする2つの別々のコードのビットは、異なるクラスにマッピングされる可能性が十分にあるのです。 しかし、同じキャプチャサイトで同じラムダを評価し、そのラムダが非キャプチャであれば、同じインスタンスを取得し、参照等価で比較することができます。
ラムダがシリアライザブルであれば、パフォーマンスとセキュリティを多少犠牲にする代わりに、より簡単に状態を放棄することができます(無料の昼食はありません)。
等号の定義を微調整することが実用的であるかもしれない 1 つの領域は、メソッド参照です。 これは現在検討中です。
あなたが得ようとしていることは、2つのラムダが同じ機能インターフェイスに変換され、同じ動作関数によって表され、同一のキャプチャされた引数を持つ場合、それらは同じである、ということだと思います。
残念ながら、これは難しいし(シリアライズ不可能なラムダでは、そのすべての構成要素を得ることはできません)、十分ではありません(2つの別々にコンパイルしたファイルが同じラムダを同じ関数インターフェイス型に変換しても、見分けがつかないからです)。
EG では、これらの判断を可能にするために十分な情報を公開するかどうか、またラムダがより選択的に実装するべきかどうかを議論しました。
equals
/
hashCode
またはもっと説明的なtoStringを使用します。 結論は、呼び出し元がこの情報を利用できるようにするためにパフォーマンス コストで何かを支払う気はないということでした (悪いトレードオフ、0.01% に利益をもたらすもののために 99.99% のユーザーを罰すること)。
に関する決定的な結論は
toString
は成立しませんでしたが、将来的に再検討される可能性は残されています。 しかし、この問題については、両側からいくつかの良い議論がなされましたので、これはスラムダンクではありません。
関連
-
無効な文字定数
-
java マイクロソフト払い戻し予期せぬサーバーからのファイルの終了
-
eclipse にリソースリーク:'in' が閉じない
-
Java の double データ型における 0.0 と -0.0 の問題
-
[解決済み] Java の配列を表示する最も簡単な方法は何ですか?
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?
-
[解決済み] 整数の平方根が整数であるかどうかを判断する最速の方法
-
[解決済み] HashMapを直接(リテラルに)初期化する方法は?
-
[解決済み] Java 8でインデックスを持つストリームを反復処理する簡潔な方法はありますか?
-
[解決済み] Pythonのlambdaで "if "を実行する方法はありますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Collections.sortがdoubleでソートできない問題を完璧に解決する。
-
Java の switch case 文で必要な定数式の問題の解決法
-
Uncaught ReferenceError: は定義されていません。
-
無効なメソッド宣言
-
SpringBoot 起動エラー java.nio.charset.MalformedInputException: 入力長 = 2 解決
-
コンストラクタDate()が未定義である問題
-
代入の左辺は変数でなければならない 解答
-
Java JDKのダイナミックプロキシ(AOP)の使用と実装の原理分析
-
swagger2 モデルが表示されない モデルが見つからない @ApiModel アノテーションが表示されない問題
-
[解決済み] Java8: java.lang.Objectのメソッドにデフォルトメソッドを定義することはなぜ禁じられているのでしょうか?