537 Stimmen

Wie übergibt man eine Funktion als Parameter in Java?

Wie kann man in Java eine Funktion als Argument einer anderen Funktion übergeben?

590voto

jk. Punkte 7293

Java 8 und höher

Wenn Sie mit Java 8+ Lambda-Ausdrücke verwenden, können Sie eine Klasse oder Schnittstelle mit nur einer einzigen abstrakten Methode (manchmal als SAM-Typ ), zum Beispiel:

public interface MyInterface {
    String doSomething(int param1, String param2);
}

dann können Sie überall dort, wo MyInterface verwendet wird, einen Lambda-Ausdruck einsetzen:

class MyClass {
    public MyInterface myInterface = (p1, p2) -> { return p2 + p1; };
}

Sie können zum Beispiel sehr schnell ein neues Thema erstellen:

new Thread(() -> someMethod()).start();

Und verwenden Sie die Methodenreferenzsyntax um es noch sauberer zu machen:

new Thread(this::someMethod).start();

Ohne Lambda-Ausdrücke, würden die letzten beiden Beispiele wie folgt aussehen:

new Thread(new Runnable() { someMethod(); }).start();

Vor Java 8

Ein gängiges Muster wäre, sie in eine Schnittstelle zu "verpacken", wie Callable zum Beispiel, dann übergeben Sie ein Callable:

public T myMethod(Callable<T> func) {
    return func.call();
}

Dieses Muster ist bekannt als die Befehlsmuster .

Denken Sie daran, dass es am besten wäre, eine Schnittstelle für Ihre spezielle Verwendung zu erstellen. Wenn Sie sich für callable entschieden haben, dann ersetzen Sie T oben durch den Rückgabewert, den Sie erwarten, z. B. String.

Als Antwort auf Ihren unten stehenden Kommentar könnten Sie sagen:

public int methodToPass() { 
        // do something
}

public void dansMethod(int i, Callable<Integer> myFunc) {
       // do something
}

und rufen Sie sie dann auf, vielleicht unter Verwendung einer anonymen inneren Klasse:

dansMethod(100, new Callable<Integer>() {
   public Integer call() {
        return methodToPass();
   }
});

Denken Sie daran, dass dies kein "Trick" ist. Es ist nur das grundlegende konzeptionelle Äquivalent von Java zu Funktionszeigern.

132voto

bdoughan Punkte 144925

Sie könnten dazu Java-Reflection verwenden. Die Methode würde als eine Instanz von java.lang.reflect.Method .

import java.lang.reflect.Method;

public class Demo {

    public static void main(String[] args) throws Exception{
        Class[] parameterTypes = new Class[1];
        parameterTypes[0] = String.class;
        Method method1 = Demo.class.getMethod("method1", parameterTypes);

        Demo demo = new Demo();
        demo.method2(demo, method1, "Hello World");
    }

    public void method1(String message) {
        System.out.println(message);
    }

    public void method2(Object object, Method method, String message) throws Exception {
        Object[] parameters = new Object[1];
        parameters[0] = message;
        method.invoke(object, parameters);
    }

}

106voto

The Guy with The Hat Punkte 10215

Lambda-Ausdrücke

Zur Ergänzung von jk.'s ausgezeichnete Antwort können Sie jetzt eine Methode einfacher übergeben, indem Sie Lambda-Ausdrücke (in Java 8). Zunächst einige Hintergrundinformationen. A funktionale Schnittstelle ist eine Schnittstelle, die nur eine einzige abstrakte Methode hat, obwohl sie eine beliebige Anzahl von Methoden enthalten kann Standard-Methoden (neu in Java 8) und statische Methoden. Ein Lambda-Ausdruck kann die abstrakte Methode schnell implementieren, ohne die ganze unnötige Syntax, die erforderlich ist, wenn Sie keinen Lambda-Ausdruck verwenden.

Ohne Lambda-Ausdrücke:

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});

Mit Lambda-Ausdrücken:

obj.aMethod(i -> i == 982);

Hier ist ein Auszug aus das Java-Tutorial über Lambda-Ausdrücke :

Syntax von Lambda-Ausdrücken

Ein Lambda-Ausdruck besteht aus Folgendem:

  • Eine durch Komma getrennte Liste von formalen Parametern, die in Klammern eingeschlossen sind. Die Methode CheckPerson.test enthält einen Parameter, p, der eine Instanz der Klasse Person darstellt.

    Nota : Sie können den Datentyp der Parameter in einem Lambda-Ausdruck weglassen. In Außerdem können Sie die Klammern weglassen, wenn es nur einen Parameter gibt. Der folgende Lambda-Ausdruck ist zum Beispiel auch gültig:

    p -> p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
  • Das Pfeilsymbol, ->

  • Ein Körper, der aus einem einzelnen Ausdruck oder einem Anweisungsblock besteht. In diesem Beispiel wird der folgende Ausdruck verwendet:

    p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25

    Wenn Sie einen einzelnen Ausdruck angeben, wertet die Java-Laufzeitumgebung den Ausdruck aus und gibt dann seinen Wert zurück. Alternativ dazu können Sie eine Return-Anweisung verwenden:

    p -> {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }

    Eine return-Anweisung ist kein Ausdruck; in einem Lambda-Ausdruck müssen Sie die Anweisungen in geschweifte Klammern ({}) einschließen. Sie müssen jedoch nicht einen ungültigen Methodenaufruf nicht in geschweifte Klammern einschließen. Zum Beispiel kann die ist ein gültiger Lambda-Ausdruck:

    email -> System.out.println(email)

Beachten Sie, dass ein Lambda-Ausdruck einer Methodendeklaration sehr ähnlich ist; Sie können Lambda-Ausdrücke als anonyme Methoden betrachten - Methoden ohne Namen betrachten.


So können Sie eine Methode mit einem Lambda-Ausdruck "übergeben":

Hinweis: Hier wird eine neue funktionale Standardschnittstelle verwendet, java.util.function.IntConsumer .

class A {
    public static void methodToPass(int i) { 
        // do stuff
    }
}

import java.util.function.IntConsumer;

class B {
    public void dansMethod(int i, IntConsumer aMethod) {
        /* you can now call the passed method by saying aMethod.accept(i), and it
        will be the equivalent of saying A.methodToPass(i) */
    }
}

class C {
    B b = new B();

    public C() {
        b.dansMethod(100, j -> A.methodToPass(j));   //Lambda Expression here
    }
}

Das obige Beispiel kann noch weiter verkürzt werden, indem man die :: Betreiber .

public C() {
    b.dansMethod(100, A::methodToPass);
}

20voto

Nathan Hughes Punkte 90344

Dank Java 8 müssen Sie die folgenden Schritte nicht mehr durchführen, um eine Funktion an eine Methode zu übergeben, dafür gibt es Lambdas, siehe Oracle's Lambda-Ausdruck-Tutorial . Der Rest dieses Beitrags beschreibt, was wir in den schlechten alten Zeiten tun mussten, um diese Funktion zu implementieren.

Normalerweise deklarieren Sie Ihre Methode als eine Schnittstelle mit einer einzelnen Methode und übergeben dann ein Objekt, das diese Schnittstelle implementiert. Ein Beispiel dafür sind die Commons-Sammlungen, die über Schnittstellen für Closure, Transformer und Predicate verfügen, sowie über Methoden, denen Sie Implementierungen dieser Schnittstellen übergeben. Guava ist die neue, verbesserte commons-collections, Sie können dort entsprechende Schnittstellen finden.

So hat commons-collections zum Beispiel org.apache.commons.collections.CollectionUtils, das viele statische Methoden hat, die übergebene Objekte nehmen, um eine zufällig auszuwählen, gibt es eine namens exists mit dieser Signatur:

static boolean exists(java.util.Collection collection, Predicate predicate) 

Es nimmt ein Objekt, das die Schnittstelle Predicate implementiert, was bedeutet, dass es eine Methode haben muss, die ein Objekt annimmt und einen Boolean zurückgibt.

Ich kann es also so nennen:

CollectionUtils.exists(someCollection, new Predicate() {
    public boolean evaluate(Object object) { 
        return ("a".equals(object.toString());
    }
});

und gibt true oder false zurück, je nachdem, ob someCollection enthält ein Objekt, für das das Prädikat "wahr" ergibt.

Wie auch immer, dies ist nur ein Beispiel, und commons-collections ist veraltet. Ich habe einfach das Äquivalent in Guava vergessen.

7voto

amalloy Punkte 82950

Java unterstützt Schließungen sehr gut. Es unterstützt nur nicht Funktionen Daher ist die Syntax, die Sie für Closures gewohnt sind, viel umständlicher und sperriger: Sie müssen alles in einer Klasse mit einer Methode verpacken. Zum Beispiel,

public Runnable foo(final int x) {
  return new Runnable() {
    public void run() {
      System.out.println(x);
    }
  };
}

Gibt ein Runnable-Objekt zurück, dessen run() Methode "schließt über" die x übergeben, genau wie in jeder Sprache, die Funktionen erster Klasse und Schließungen unterstützt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X