Gibt es eine Möglichkeit zu vergleichen lambdas?
Sagen, ich habe eine Liste von Objekten definiert wurden lambda-Ausdrücke (closures). Gibt es eine Möglichkeit, diese zu überprüfen, so dass Sie verglichen werden kann?
Den code, ich bin am meisten interessiert, ist
List<Strategy> strategies = getStrategies();
Strategy a = (Strategy) this::a;
if (strategies.contains(a)) { //...
Den vollständigen code
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";
}
}
Scheint es die einzige Lösung ist, definieren Sie die einzelnen lambda als ein Feld, und verwenden Sie nur diese Felder. Wenn Sie möchten, drucken Sie die Methode aufgerufen haben, sind Sie besser dran mit Method
. Gibt es eine bessere Möglichkeit mit lambda-Ausdrücken?
Außerdem ist es möglich zu drucken, eine lambda und etwas lesbarer? Wenn Sie drucken this::a
statt
ClosureEqualsMain$$Lambda$1/821270929@3f99bd52
etwas wie
ClosureEqualsMain.a()
oder sogar this.toString
und die Methode.
my-ClosureEqualsMain.a();
Können Sie ein Beispiel geben, das kompiliert?
Da
toString
definiert ist Object
ich denke, definieren Sie eine Schnittstelle, die liefert eine default-Implementierung von toString
ohne Verletzung der -Methode Voraussetzung für die Schnittstellen funktional sein. Ich habe nicht geprüft, obwohl dies.Das ist falsch. Klassen Erben nicht ein Standard-Objekt deklarierten Methoden in Schnittstellen; siehe stackoverflow.com/questions/24016962/... für die Erklärung.
Danke für den Zeiger.
InformationsquelleAutor Peter Lawrey | 2014-06-07
Du musst angemeldet sein, um einen Kommentar abzugeben.
Diese Frage interpretiert werden könnten, relativ zu der Spezifikation oder der Implementierung. Offensichtlich Implementierungen ändern könnte, aber Sie könnten bereit sein, ändern Sie Ihren code, wenn das geschieht, so werde ich die Antwort auf beides.
Es hängt auch davon ab, was Sie tun möchten. Sind Sie auf der Suche zu optimieren, oder sind Sie auf der Suche nach ironclad garantiert, dass zwei Instanzen (oder nicht) die gleiche Funktion? (Wenn letzteres, wirst du dich an der Verschiedenheit mit computational physics, in, dass sogar Probleme so einfach sein wie die Frage, ob zwei Funktionen berechnen Sie die gleiche Sache sind unentscheidbaren.)
Aus einer Spezifikation Perspektive, die Sprache, die spec verspricht nur, dass das Ergebnis der Auswertung (nicht berufen) ein lambda-Ausdruck ist eine Instanz einer Klasse die das Ziel der funktionalen Schnittstelle. Es macht keine Versprechungen über die Identität, oder Grad des aliasing, der das Ergebnis. Dies ist beabsichtigt, zu geben, Implementierungen, maximale Flexibilität zu bieten, eine bessere Leistung (dies ist, wie lambdas schneller sein können als innere Klassen; wir sind nicht gebunden an die "erstellen müssen eindeutige Instanz" die Einschränkung, dass innere Klassen sind.)
Also im Grunde ist die Skillung nicht viel, außer natürlich, dass zwei lambdas, die sind Referenz-Gleichheit ( = = ), um zu berechnen die gleiche Funktion.
Aus einer Umsetzung Perspektive, können Sie davon ausgehen, ein wenig mehr. Es gibt (aktuell, kann sich ändern), eine 1:1-Beziehung zwischen den synthetischen Klassen, die zur Umsetzung von lambdas, und die capture-sites im Programm. Also zwei separate bits des Codes, die erfassen, "x -> x + 1" kann auch zugeordnet werden, um verschiedene Klassen. Aber wenn Sie bewerten den gleichen lambda auf das gleiche capture-Seite ist, und dass die lambda nicht erfassen, Sie bekommen die gleiche Instanz, die verglichen werden können mit Verweis der Geschlechter.
Wenn Ihr lambdas serialisierbar sind, werden Sie geben Ihrem Staat leichter, im Austausch für den Verzicht auf einige performance-und Sicherheits - (no free lunch.)
Einem Bereich, wo es vielleicht praktisch zu zwicken die definition der Gleichheit ist mit der Methode verweisen, da diese es Ihnen ermöglichen würde, verwendet werden, als Zuhörer und ordnungsgemäß registriert. Dies ist unter Berücksichtigung.
Ich denke, was Sie versuchen, zu bekommen ist: wenn zwei Lambda-Ausdrücke umgewandelt werden, um die gleiche funktionale Schnittstelle, vertreten durch das gleiche Verhalten der Funktion, und haben identisch erfasst args, Sie sind die gleichen
Leider ist dies sowohl schwer zu tun (für nicht-serialisierbare lambdas, können Sie nicht an alle Komponenten) und nicht genügend (da zwei separat kompilierten Dateien konvertieren konnte der gleiche lambda-Ausdruck die gleiche funktionale Schnittstelle geben, und Sie wäre nicht in der Lage zu sagen.)
Dem Z.B. diskutiert, ob zu entlarven genug Informationen, um diese Entscheidungen zu treffen, sowie darüber zu diskutieren, ob Lambda-Ausdrücke einführen sollte, die selektiver equals/hashCode oder mehrere beschreibende toString. Die Schlussfolgerung war, dass wir nicht bereit waren, etwas zu bezahlen, in der Leistung Kosten, diese Informationen an den Aufrufer (schlechten Kompromiss, zu bestrafen, 99,99% der Benutzer für etwas, das Vorteile .01%).
Einer endgültigen Bewertung toString nicht erreicht wurde, aber offen gelassen werden, revisited, in die Zukunft. Dennoch gab es einige gute Argumente auf beiden Seiten zu diesem Thema; das ist nicht ein slam-dunk.
==
die Gleichheit ist ein schwieriges problem zu lösen, im Allgemeinen, ich würde denken, es wäre einfach Fälle, wo der compiler, wenn nicht die JVM konnte erkennen, dassthis::a
auf einer Zeile ist das gleiche wiethis::a
auf einer anderen Linie. In der Tat ist es immer noch mir nicht klar, was Sie gewinnen, indem jeder Aufruf der Seite ist es eine eigene Implementierung. Vielleicht können Sie optimiert werden anders, aber ich hätte gedacht, dass inlining dies tun könnte.??Wie
Array
undArrays
utility claases für arrays, wie Sie konnte nicht bekommen eine anständige equals, hashCode oder toString, kann ich mir vorstellen, eineClosures
utility-Klasse eines Tages. So gibt es Sprachen, wo Sie drucken können und array und sehen Sie die Inhalte, ich glaube, es gibt Sprachen, wo Sie drucken können, Verschlüsse und bekommen einen Einblick, was die Schließung tut. (Möglicherweise eine Zeichenfolge der code ist besser, aber unbefriedigend)Wir prüften eine Anzahl von möglichen Implementierungen, einschließlich einer, eines, in dem die proxy-Klassen wurden geteilt über callsites. Die eine ging mit jetzt (ein großer Vorteil von "metafactory" - Ansatz ist, dass dieser verändert werden kann, ohne ein neues Benutzer-classfiles) war die einfachste und beste Leistung. Wir werden weiter beobachten relative performance zwischen den Optionen, wie der VM sich entwickelt, und wenn ein anderer schneller ist, werden wir wechseln.
Als seitliche Anmerkung, auch die
MethodHandle
N in der zugrunde liegenden binary interface sind nicht garantiert canonical. Also die meta-Fabrik kann nicht sagen, ob zwei Referenzen auf die gleiche Methode, indem nur der Vergleich der Referenzen. Es musste eine tiefer gehende Analyse, wenn es versucht, die Rückkehr der gleichen Umsetzung für gleichwertige Methode Referenzen.Keine änderungen für Java 9.
InformationsquelleAutor Brian Goetz
Vergleichen labmdas ich in der Regel lassen die Schnittstelle erweitern
Serializable
zu vergleichen und dann die serialisierten bytes. Nicht sehr schön, aber funktioniert für die meisten Fälle.InformationsquelleAutor KIC
Sehe ich keine Möglichkeit, um diese Informationen von der Schließung selbst.
Die Verschlüsse nicht Stand.
Aber Sie können die Verwendung von Java-Reflection, wenn Sie wollen, um zu überprüfen und vergleichen der Methoden.
Das ist natürlich keine sehr schöne Lösung, da die Leistung und die Ausnahmen, die zu fangen. Aber auf diese Weise erhalten Sie die meta-Informationen.
this
alsarg$1
aber nicht vergleichen die Methode aufgerufen. Ich kann Lesen, byte-code, um zu sehen, ob es die gleiche ist.InformationsquelleAutor F. Böller