Ich habe eine Methode, die ungefähr zehn Codezeilen umfasst. Ich möchte mehr Methoden erstellen, die genau dasselbe tun, mit Ausnahme einer kleinen Berechnung, die eine Codezeile ändert. Dies ist eine perfekte Anwendung zum Übergeben eines Funktionszeigers, um diese eine Zeile zu ersetzen, aber Java verfügt nicht über Funktionszeiger. Was ist meine beste Alternative?
java
closures
function-pointers
Bill die Eidechse
quelle
quelle
::
Betreiber auf der anderen Seite ...Antworten:
Anonyme innere Klasse
String
Angenommen, Sie möchten eine Funktion mit einem Parameter übergeben, der ein zurückgibtint
.Zuerst müssen Sie eine Schnittstelle mit der Funktion als einzigem Mitglied definieren, wenn Sie eine vorhandene nicht wiederverwenden können.
Eine Methode, die den Zeiger verwendet, würde nur eine
StringFunction
Instanz wie folgt akzeptieren :Und würde so heißen:
EDIT: In Java 8 können Sie es mit einem Lambda-Ausdruck aufrufen:
quelle
Für jeden "Funktionszeiger" würde ich eine kleine Funktorklasse erstellen , die Ihre Berechnung implementiert. Definieren Sie eine Schnittstelle, die von allen Klassen implementiert wird, und übergeben Sie Instanzen dieser Objekte an Ihre größere Funktion. Dies ist eine Kombination aus " Befehlsmuster " und " Strategiemuster ".
@ sblundys Beispiel ist gut.
quelle
Wenn es eine vordefinierte Anzahl verschiedener Berechnungen gibt, die Sie in dieser einen Zeile ausführen können, ist die Verwendung einer Aufzählung eine schnelle und dennoch klare Möglichkeit, ein Strategiemuster zu implementieren.
Offensichtlich sind die Strategie-Methodendeklaration sowie genau eine Instanz jeder Implementierung in einer einzigen Klasse / Datei definiert.
quelle
Sie müssen eine Schnittstelle erstellen, die die Funktion (en) bereitstellt, die Sie weitergeben möchten. z.B:
Wenn Sie dann eine Funktion übergeben müssen, können Sie diese Schnittstelle implementieren:
Schließlich verwendet die Kartenfunktion die in Funktion1 übergebene wie folgt:
Sie können Runnable häufig anstelle Ihrer eigenen Schnittstelle verwenden, wenn Sie keine Parameter übergeben müssen, oder Sie können verschiedene andere Techniken verwenden, um die Anzahl der Parameter weniger "fest" zu machen, aber dies ist normalerweise ein Kompromiss mit der Typensicherheit. (Oder Sie können den Konstruktor überschreiben, damit Ihr Funktionsobjekt die Parameter auf diese Weise übergibt. Es gibt viele Ansätze, und einige funktionieren unter bestimmten Umständen besser.)
quelle
Methodenreferenzen mit dem
::
OperatorSie können Methodenreferenzen in Methodenargumenten verwenden, bei denen die Methode eine funktionale Schnittstelle akzeptiert . Eine funktionale Schnittstelle ist eine Schnittstelle, die nur eine abstrakte Methode enthält. (Eine funktionale Schnittstelle kann eine oder mehrere Standardmethoden oder statische Methoden enthalten.)
IntBinaryOperator
ist eine funktionale Schnittstelle. Seine abstrakte MethodeapplyAsInt
akzeptiert zweiint
s als Parameter und gibt ein zurückint
.Math.max
akzeptiert auch zweiint
s und gibt ein zurückint
. In diesem Beispiel werdenA.method(Math::max);
dieparameter.applyAsInt
beiden Eingabewerte an gesendetMath.max
und das Ergebnis zurückgegebenMath.max
.Im Allgemeinen können Sie Folgendes verwenden:
anstatt:
das ist die Abkürzung für:
Weitere Informationen finden Sie unter Operator :: (Doppelpunkt) in Java 8 und Java Language Specification §15.13 .
quelle
Sie können dies auch tun (was in einigen seltenen Fällen sinnvoll ist). Das Problem (und es ist ein großes Problem) ist, dass Sie die Sicherheit einer Klasse / Schnittstelle verlieren und sich mit dem Fall befassen müssen, in dem die Methode nicht vorhanden ist.
Es hat den "Vorteil", dass Sie Zugriffsbeschränkungen ignorieren und private Methoden aufrufen können (im Beispiel nicht gezeigt, aber Sie können Methoden aufrufen, die der Compiler normalerweise nicht aufrufen lässt).
Auch hier ist es selten, dass dies sinnvoll ist, aber in diesen Fällen ist es ein gutes Werkzeug.
quelle
Wenn Sie nur eine andere Zeile haben, können Sie einen Parameter wie ein Flag und eine if (flag) -Anweisung hinzufügen, die die eine oder andere Zeile aufruft.
quelle
Vielleicht möchten Sie auch etwas über die Arbeit an Java 7 erfahren, bei der es um Schließungen geht:
Wie ist der aktuelle Stand der Schließungen in Java?
http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures
quelle
Neue Java 8- Funktionsschnittstellen und Methodenreferenzen mit dem
::
Operator.Java 8 kann Methodenreferenzen (MyClass :: new) mit Zeigern " @ Functional Interface " verwalten. Es ist nicht derselbe Methodenname erforderlich, sondern nur die gleiche Methodensignatur.
Beispiel:
Also, was haben wir hier?
SIE SOLLTEN NUR FÜR LISTENER FUNKTIONALE SCHNITTSTELLEN UND NUR FÜR DIESE VERWENDEN!
Weil alle anderen derartigen Funktionszeiger für die Lesbarkeit des Codes und die Fähigkeit zum Verstehen wirklich schlecht sind. Direkte Methodenreferenzen sind jedoch manchmal nützlich, beispielsweise bei foreach.
Es gibt mehrere vordefinierte funktionale Schnittstellen:
Für frühere Java-Versionen sollten Sie Guava-Bibliotheken ausprobieren, die ähnliche Funktionen und Syntax haben, wie Adrian Petrescu oben erwähnt hat.
Weitere Informationen finden Sie im Java 8 Cheatsheet
und danke an The Guy with The Hat für die Java-Sprachspezifikation §15.13 Link.
quelle
Die Antwort von @ sblundy ist großartig, aber anonyme innere Klassen weisen zwei kleine Fehler auf, wobei der primäre darin besteht, dass sie nicht wiederverwendbar sind und der sekundäre eine umfangreiche Syntax ist.
Das Schöne ist, dass sein Muster ohne Änderung in der Hauptklasse (die die Berechnungen durchführt) zu vollständigen Klassen erweitert wird.
Wenn Sie eine neue Klasse instanziieren, können Sie Parameter an diese Klasse übergeben, die als Konstanten in Ihrer Gleichung fungieren können. Wenn also eine Ihrer inneren Klassen so aussieht:
aber manchmal brauchen Sie eine, die ist:
und vielleicht ein dritter:
Anstatt zwei anonyme innere Klassen zu erstellen oder einen "Passthrough" -Parameter hinzuzufügen, können Sie eine einzelne ACTUAL-Klasse erstellen, die Sie instanziieren als:
Es würde einfach die Konstante in der Klasse speichern und sie in der in der Schnittstelle angegebenen Methode verwenden.
Wenn Sie wissen, dass Ihre Funktion nicht gespeichert / wiederverwendet wird, können Sie Folgendes tun:
Aber unveränderliche Klassen sind sicherer - ich kann keine Rechtfertigung dafür finden, eine Klasse wie diese veränderlich zu machen.
Ich poste das wirklich nur, weil ich zusammenzucke, wenn ich anonyme innere Klasse höre - ich habe viel redundanten Code gesehen, der "Erforderlich" war, weil der Programmierer als erstes anonym ging, wenn er eine tatsächliche Klasse hätte verwenden sollen und niemals überlegte seine Entscheidung.
quelle
Die Google Guava-Bibliotheken , die immer beliebter werden, verfügen über ein generisches Funktions- und Prädikatobjekt , das sie in vielen Teilen ihrer API bearbeitet haben.
quelle
Klingt für mich nach einem Strategiemuster. Schauen Sie sich die Java-Muster von fluffycat.com an.
quelle
Eines der Dinge, die ich beim Programmieren in Java wirklich vermisse, sind Funktionsrückrufe. Eine Situation, in der sich die Notwendigkeit für diese immer wieder zeigte, war die rekursive Verarbeitung von Hierarchien, in denen Sie für jedes Element eine bestimmte Aktion ausführen möchten. Zum Beispiel durch einen Verzeichnisbaum gehen oder eine Datenstruktur verarbeiten. Der Minimalist in mir hasst es, eine Schnittstelle und dann eine Implementierung für jeden speziellen Fall definieren zu müssen.
Eines Tages fragte ich mich, warum nicht? Wir haben Methodenzeiger - das Methodenobjekt. Mit der Optimierung von JIT-Compilern bringt der reflektierende Aufruf keine großen Leistungseinbußen mehr mit sich. Abgesehen davon, dass beispielsweise eine Datei von einem Speicherort an einen anderen kopiert wird, sind die Kosten für den Aufruf der reflektierten Methode unbedeutend.
Als ich mehr darüber nachdachte, wurde mir klar, dass ein Rückruf im OOP-Paradigma das Zusammenbinden eines Objekts und einer Methode erfordert - geben Sie das Rückrufobjekt ein.
Schauen Sie sich meine reflexionsbasierte Lösung für Rückrufe in Java an . Kostenlos für jede Verwendung.
quelle
OK, dieser Thread ist bereits alt genug, daher ist meine Antwort sehr wahrscheinlich nicht hilfreich für die Frage. Aber da dieser Thread mir geholfen hat, meine Lösung zu finden, werde ich ihn trotzdem hier veröffentlichen.
Ich musste eine variable statische Methode mit bekannter Eingabe und bekannter Ausgabe (beide doppelt ) verwenden. Wenn ich also das Methodenpaket und den Namen kenne, könnte ich wie folgt arbeiten:
für eine Funktion, die ein Double als Parameter akzeptiert.
Also habe ich es in meiner konkreten Situation mit initialisiert
und rief es später in einer komplexeren Situation mit
wobei Aktivität ein beliebiger Doppelwert ist. Ich denke darüber nach, dies vielleicht etwas abstrakter zu machen und zu verallgemeinern, wie es SoftwareMonkey getan hat, aber derzeit bin ich mit dem, was es ist, zufrieden genug. Drei Codezeilen, keine Klassen und Schnittstellen erforderlich, das ist nicht schlecht.
quelle
code
Abschlags, ich war zu ungeduldig und dumm, um ihn zu findenSo machen Sie dasselbe ohne Schnittstellen für eine Reihe von Funktionen:
quelle
Schauen Sie sich Lambdaj an
http://code.google.com/p/lambdaj/
und insbesondere seine neue Verschlussfunktion
http://code.google.com/p/lambdaj/wiki/Closures
und Sie werden eine sehr lesbare Möglichkeit finden, einen Abschluss oder einen Funktionszeiger zu definieren, ohne eine bedeutungslose Schnittstelle zu erstellen oder hässliche innere Klassen zu verwenden
quelle
Wow, warum nicht einfach eine Delegate-Klasse erstellen, die nicht allzu schwer ist, da ich dies bereits für Java getan habe, und damit Parameter übergeben, bei denen T der Rückgabetyp ist. Es tut mir leid, aber als C ++ / C # -Programmierer im Allgemeinen, der gerade Java lernt, brauche ich Funktionszeiger, weil sie sehr praktisch sind. Wenn Sie mit einer Klasse vertraut sind, die sich mit Methodeninformationen befasst, können Sie dies tun. In Java-Bibliotheken wäre das java.lang.reflect.method.
Wenn Sie immer eine Schnittstelle verwenden, müssen Sie diese immer implementieren. Bei der Ereignisbehandlung gibt es keinen besseren Weg, um sich von der Liste der Handler zu registrieren / die Registrierung aufzuheben, als für Delegaten, bei denen Sie Funktionen und nicht den Werttyp übergeben müssen, sodass eine Delegatenklasse diese für Outclasses einer Schnittstelle handhabt.
quelle
Keine der Java 8-Antworten hat ein vollständiges, zusammenhängendes Beispiel gegeben.
Deklarieren Sie die Methode, die den "Funktionszeiger" akzeptiert, wie folgt:
Rufen Sie es auf, indem Sie der Funktion einen Lambda-Ausdruck geben:
quelle
Wenn jemand Schwierigkeiten hat, eine Funktion zu übergeben, die einen Parametersatz benötigt, um ihr Verhalten zu definieren, aber einen anderen Parametersatz, der ausgeführt werden soll, wie z.
Siehe Übergabe der Funktion mit parameterdefiniertem Verhalten in Java
quelle
Seit Java8 können Sie Lambdas verwenden, die auch Bibliotheken in der offiziellen SE 8-API enthalten.
Verwendung: Sie müssen eine Schnittstelle mit nur einer abstrakten Methode verwenden. Erstellen Sie eine Instanz davon (möglicherweise möchten Sie die bereits bereitgestellte Java SE 8 verwenden) wie folgt:
Weitere Informationen finden Sie in der Dokumentation: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
quelle
Vor Java 8 war eine anonyme Klasse der nächste Ersatz für funktionszeigerähnliche Funktionen. Zum Beispiel:
Aber jetzt haben wir in Java 8 eine sehr nette Alternative, die als Lambda-Ausdruck bekannt ist und verwendet werden kann als:
Dabei ist isBiggerThan eine Methode in
CustomClass
. Wir können hier auch Methodenreferenzen verwenden:quelle