wake-up-neo.com

Funktionszeiger in Java

Dies mag etwas Gemeines und Triviales sein, aber ich habe anscheinend Probleme, eine konkrete Antwort zu finden. In C # gibt es ein Konzept von Delegaten, das sich stark auf die Idee von Funktionszeigern aus C++ bezieht. Gibt es eine ähnliche Funktionalität in Java? Was ist angesichts der Tatsache, dass Zeiger etwas fehlen, der beste Weg, dies zu tun? Und um klar zu sein, wir reden hier erstklassig.

147
Ben Lakey

Das Java - Idiom für funktionszeigerähnliche Funktionalität ist eine anonyme Klasse, die eine Schnittstelle implementiert, z.

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

pdate: das obige ist notwendig in Java Versionen vor Java) 8. Jetzt haben wir viel schönere Alternativen, nämlich Lambdas:

list.sort((a, b) -> a.isGreaterThan(b));

und Methodenreferenzen:

list.sort(MyClass::isGreaterThan);
125

Sie können einen Funktionszeiger durch eine Schnittstelle ersetzen. Nehmen wir an, Sie möchten eine Sammlung durchgehen und mit jedem Element etwas anfangen.

public interface IFunction {
  public void execute(Object o);
}

Dies ist die Schnittstelle, die wir beispielsweise an CollectionUtils2.doFunc (Collection c, IFunction f) übergeben könnten.

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

Nehmen wir als Beispiel an, wir haben eine Sammlung von Zahlen und Sie möchten jedem Element 1 hinzufügen.

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});
63
raupach

Sie können Reflexion verwenden, um es zu tun.

Übergeben Sie als Parameter das Objekt und den Methodennamen (als String) und rufen Sie dann die Methode auf. Beispielsweise:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

Und dann benutze es wie in:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

Überprüfen Sie natürlich alle Ausnahmen und fügen Sie die erforderlichen Besetzungen hinzu.

41
zoquete

Nein, Funktionen sind in Java keine erstklassigen Objekte. Sie können dasselbe tun, indem Sie eine Handler-Klasse implementieren - so werden Callbacks in Swing usw. implementiert.

Es gibt jedoch Vorschläge für Schließungen (der offizielle Name für das, wovon Sie sprechen) in zukünftigen Versionen von Java - Javaworld hat einen interessanten Artikel.

20
jwoolard

Dies erinnert an Steve Yegges Hinrichtung im Königreich der Nomen . Grundsätzlich heißt es, dass Java für jede Aktion ein Objekt benötigt und daher keine "Nur-Verb" -Entitäten wie Funktionszeiger hat.

15
Yuval F

Um ähnliche Funktionen zu erreichen, können Sie anonyme innere Klassen verwenden.

Wenn Sie eine Schnittstelle Foo definieren würden:

interface Foo {
    Object myFunc(Object arg);
}

Erstellen Sie eine Methode bar, die als Argument einen 'Funktionszeiger' erhält:

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

Rufen Sie die Methode abschließend wie folgt auf:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}
8
Kingamajick

Java8 hat Lambdas und Methodenreferenzen eingeführt. Wenn Ihre Funktion also mit einem Funktionsschnittstelle (Sie können Ihre eigene erstellen) übereinstimmt, können Sie in diesem Fall eine Methodenreferenz verwenden.

Java bietet eine Reihe von gängigen funktionalen Schnittstellen . während Sie Folgendes tun könnten:

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}
6
Alex

In Java gibt es so etwas nicht. Sie müssen Ihre Funktion in ein Objekt einbinden und die Referenz an dieses Objekt übergeben, um die Referenz an die Methode für dieses Objekt zu übergeben.

Syntaktisch kann dies bis zu einem gewissen Grad vereinfacht werden, indem anonyme Klassen verwendet werden, die an Ort und Stelle definiert sind, oder anonyme Klassen, die als Mitgliedsvariablen der Klasse definiert sind.

Beispiel:

class MyComponent extends JPanel {
    private JButton button;
    public MyComponent() {
        button = new JButton("click me");
        button.addActionListener(buttonAction);
        add(button);
    }

    private ActionListener buttonAction = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // handle the event...
            // note how the handler instance can access 
            // members of the surrounding class
            button.setText("you clicked me");
        }
    }
}
5
VoidPointer

Ich habe Callback/Delegate-Unterstützung in Java mithilfe von Reflection implementiert. Details und Arbeitsquelle sind auf meiner Website verfügbar .

Wie es funktioniert

Wir haben eine Hauptklasse namens Callback mit einer verschachtelten Klasse namens WithParms. Die API, die den Rückruf benötigt, nimmt ein Callback-Objekt als Parameter und erstellt bei Bedarf eine Callback.WithParms als Methodenvariable. Da sehr viele Anwendungen dieses Objekts rekursiv sind, funktioniert dies sehr sauber.

Da die Leistung für mich immer noch einen hohen Stellenwert hat, wollte ich nicht für jeden Aufruf ein Einweg-Objekt-Array erstellen, in dem die Parameter gespeichert werden. Schließlich kann es in einer großen Datenstruktur Tausende von Elementen und in einer Nachrichtenverarbeitung geben Szenario könnten wir am Ende Tausende von Datenstrukturen pro Sekunde verarbeiten.

Um threadsicher zu sein, muss das Parameter-Array für jeden Aufruf der API-Methode eindeutig vorhanden sein, und aus Effizienzgründen sollte für jeden Aufruf des Rückrufs dasselbe verwendet werden. Ich brauchte ein zweites Objekt, das billig zu erstellen wäre, um den Rückruf mit einem Parameterarray für den Aufruf zu verbinden. In einigen Szenarien verfügt der Aufrufer jedoch aus anderen Gründen bereits über ein Parameterarray. Aus diesen beiden Gründen gehörte das Parameterarray nicht zum Callback-Objekt. Auch die Auswahl des Aufrufs (Übergabe der Parameter als Array oder als einzelne Objekte) liegt in den Händen der API, indem der Rückruf verwendet wird, um den Aufruf zu verwenden, der für das Innenleben am besten geeignet ist.

Die WithParms-verschachtelte Klasse ist also optional und dient zwei Zwecken. Sie enthält das für die Rückrufaufrufe erforderliche Parameterobjektarray und stellt 10 überladene invoke () -Methoden (mit 1 bis 10 Parametern) bereit, die das Parameterarray laden und dann Rufen Sie das Rückrufziel auf.

3
Lawrence Dol

Überprüfen Sie die Verschlüsse, wie sie in der Lambdaj-Bibliothek implementiert wurden. Sie haben tatsächlich ein ähnliches Verhalten wie C # -Delegierte:

http://code.google.com/p/lambdaj/wiki/Closures

2
Mario Fusco

Relativ zu den meisten Leuten hier bin ich neu in Java), aber da ich keinen ähnlichen Vorschlag gesehen habe, habe ich eine andere Alternative vorzuschlagen. Ich bin mir nicht sicher, ob es eine gute Praxis ist oder nicht oder sogar vorgeschlagen vor und ich habe es einfach nicht verstanden. Ich mag es einfach, da ich denke, es ist selbstbeschreibend.

 /*Just to merge functions in a common name*/
 public class CustomFunction{ 
 public CustomFunction(){}
 }

 /*Actual functions*/
 public class Function1 extends CustomFunction{
 public Function1(){}
 public void execute(){...something here...}
 }

 public class Function2 extends CustomFunction{
 public Function2(){}
 public void execute(){...something here...}
 }

 .....
 /*in Main class*/
 CustomFunction functionpointer = null;

dann je nach anwendung zuweisen

 functionpointer = new Function1();
 functionpointer = new Function2();

etc.

und anrufen bei

 functionpointer.execute();
0
ozgeneral