wake-up-neo.net

Java Pass-Methode als Parameter

Ich suche nach einer Möglichkeit, eine Methode als Referenz zu übergeben. Ich verstehe, dass Java Methoden nicht als Parameter übergibt, ich möchte jedoch eine Alternative. 

Mir wurde gesagt, dass Schnittstellen die Alternative zum Übergeben von Methoden als Parameter sind, aber ich verstehe nicht, wie eine Schnittstelle durch Referenz als Methode dienen kann. Wenn ich richtig verstehe, ist eine Schnittstelle einfach eine abstrakte Menge von Methoden, die nicht definiert sind. Ich möchte keine Schnittstelle senden, die jedes Mal definiert werden muss, da verschiedene Methoden dieselbe Methode mit denselben Parametern aufrufen können.

Was ich erreichen möchte, ist etwas Ähnliches:

public void setAllComponents(Component[] myComponentArray, Method myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod(leaf);
    } //end looping through components
}

aufgerufen wie:

setAllComponents(this.getComponents(), changeColor());
setAllComponents(this.getComponents(), changeSize());
223
Mac

Edit: Ab Java 8 sind Lambda-Ausdrücke eine Nizza-Lösung als andereAntworten . Die Antwort wurde für Java 7 und früher geschrieben ...


Schauen Sie sich das Befehlsmuster an.

// NOTE: code not tested, but I believe this is valid Java...
public class CommandExample 
{
    public interface Command 
    {
        public void execute(Object data);
    }

    public class PrintCommand implements Command 
    {
        public void execute(Object data) 
        {
            System.out.println(data.toString());
        }    
    }

    public static void callCommand(Command command, Object data) 
    {
        command.execute(data);
    }

    public static void main(String... args) 
    {
        callCommand(new PrintCommand(), "hello world");
    }
}

Edit: as Pete Kirkham weist darauf hin , es gibt eine andere Möglichkeit, dies mit einem Visitor zu tun. Der Besucheransatz ist etwas komplizierter - alle Knoten müssen mit einer acceptVisitor()-Methode für den Besucher sensibilisiert werden. Wenn Sie jedoch einen komplexeren Objektgraph durchlaufen müssen, sollten Sie dies überprüfen.

204
Dan Vinton

In Java 8 können Sie nun eine Methode mit Lambda Expressions und Method References leichter übergeben. Zunächst einige Hintergründe: Eine funktionale Schnittstelle ist eine Schnittstelle, die nur eine einzige abstrakte Methode hat, obwohl sie eine beliebige Anzahl von Standardmethoden (neu in Java 8) und statischen Methoden enthalten kann. Ein Lambda-Ausdruck kann die abstrakte Methode schnell implementieren, ohne die unnötige Syntax, 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 dem Java-Tutorial zu Lambda Expressions :

Syntax von Lambda-Ausdrücken

Ein Lambda-Ausdruck besteht aus folgenden Elementen:

  • Eine durch Kommas getrennte Liste formaler Parameter, die in Klammern stehen. Die CheckPerson.test-Methode enthält einen Parameter, p, was eine Instanz der Person-Klasse darstellt.

    Note: Sie kann den Datentyp der Parameter in einem Lambda-Ausdruck weglassen. Im Außerdem können Sie die Klammern weglassen, wenn nur ein Parameter vorhanden ist . Beispielsweise ist auch der folgende Lambda-Ausdruck gültig:

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

  • 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 seinen Wert zurück. Alternative, Sie können eine Rückgabeanweisung 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 Anweisungen in geschweifte Klammern ({}) einschließen. Sie haben jedoch nicht einen ungültigen Methodenaufruf in geschweifte Klammern einschließen. Zum Beispiel die Folgendes ist ein gültiger Lambda-Ausdruck:

    email -> System.out.println(email)
    

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


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

interface I {
    public void myMethod(Component component);
}

class A {
    public void changeColor(Component component) {
        // code here
    }

    public void changeSize(Component component) {
        // code here
    }
}
class B {
    public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) {
        for(Component leaf : myComponentArray) {
            if(leaf instanceof Container) { // recursive call if Container
                Container node = (Container)leaf;
                setAllComponents(node.getComponents(), myMethodInterface);
            } // end if node
            myMethodsInterface.myMethod(leaf);
        } // end looping through components
    }
}
class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), component -> a.changeColor(component));
        b.setAllComponents(this.getComponents(), component -> a.changeSize(component));
    }
}

Die Klasse C kann durch die Verwendung von Methodenverweisen wie folgt noch etwas verkürzt werden:

class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), a::changeColor);
        b.setAllComponents(this.getComponents(), a::changeSize);
    }
}
54

Verwenden Sie das Java.lang.reflect.Method-Objekt und rufen Sie invoke auf.

Definieren Sie zunächst eine Schnittstelle mit der Methode, die Sie als Parameter übergeben möchten

public interface Callable {
  public void call(int param);
}

Implementieren Sie eine Klasse mit der Methode

class Test implements Callable {
  public void call(int param) {
    System.out.println( param );
  }
}

// so aufrufen

Callable cmd = new Test();

Dadurch können Sie cmd als Parameter übergeben und den in der Schnittstelle definierten Methodenaufruf aufrufen

public invoke( Callable callable ) {
  callable.call( 5 );
}
17
stacker

Während dies für Java 7 und darunter noch nicht gültig ist, glaube ich, dass wir in die Zukunft schauen und zumindest die Änderungen in neuen Versionen wie Java 8 erkennen sollten. 

Diese neue Version bringt nämlich lambdas und Methodenverweise auf Java (zusammen mit neuen APIs ), die eine andere gültige Lösung für dieses Problem darstellen. Während sie noch eine Schnittstelle benötigen, werden keine neuen Objekte erstellt und zusätzliche Klassendateien Ausgabeverzeichnisse müssen aufgrund unterschiedlicher Handhabung durch die JVM nicht verschmutzt werden. 

Beide Varianten (Lambda und Methodenreferenz) erfordern eine Schnittstelle, die mit einer einzigen Methode verfügbar ist, deren Signatur verwendet wird:

public interface NewVersionTest{
    String returnAString(Object oIn, String str);
}

Namen von Methoden werden von jetzt an keine Rolle spielen. Wenn ein Lambda akzeptiert wird, gilt auch eine Methodenreferenz. Um beispielsweise unsere Signatur hier zu verwenden:

public static void printOutput(NewVersionTest t, Object o, String s){
    System.out.println(t.returnAString(o, s));
}

Dies ist nur ein einfacher Schnittstellenaufruf, bis zum Lambda1 wird bestanden:

public static void main(String[] args){
    printOutput( (Object oIn, String sIn) -> {
        System.out.println("Lambda reached!");
        return "lambda return";
    }
    );
}

Dies wird ausgegeben:

Lambda reached!
lambda return

Methodenreferenzen sind ähnlich. Gegeben:

public class HelperClass{
    public static String testOtherSig(Object o, String s){
        return "real static method";
    }
}

und haupt:

public static void main(String[] args){
    printOutput(HelperClass::testOtherSig);
}

die Ausgabe wäre real static method. Methodenreferenzen können statisch, instanziell, nicht statisch mit beliebigen Instanzen und sogar Konstruktoren sein . Für den Konstruktor würde etwas wie ClassName::new verwendet.

1 Dies wird von manchen nicht als Lambda betrachtet, da es Nebenwirkungen hat. Es veranschaulicht jedoch die Verwendung eines Systems auf eine einfachere Weise, um es zu visualisieren.

12
Andrey Akhmetov

Beim letzten Mal konnte Java nicht das tun, was Sie wollen. Sie müssen "Workarounds" verwenden, um solche Einschränkungen zu umgehen. Schnittstellen sind meines Erachtens eine Alternative, aber keine gute Alternative. Vielleicht hat derjenige, der Ihnen gesagt hat, etwas wie das bedeutet:

public interface ComponentMethod {
  public abstract void PerfromMethod(Container c);
}

public class ChangeColor implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public class ChangeSize implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod.PerfromMethod(leaf);
    } //end looping through components
}

Was würden Sie dann aufrufen mit:

setAllComponents(this.getComponents(), new ChangeColor());
setAllComponents(this.getComponents(), new ChangeSize());
11
user246091

Wenn Sie diese Methoden nicht benötigen, um etwas zurückzugeben, können Sie sie zu ausführbaren Objekten zurückgeben.

private Runnable methodName (final int arg){
    return new Runnable(){
       public void run(){
          // do stuff with arg
       }
    }
}

Dann benutze es wie:

private void otherMethodName (Runnable arg){
    arg.run();
}
6
Smig

Seit Java 8 gibt es eine Function<T, R>-Schnittstelle ( docs ), die eine Methode hat 

R apply(T t);

Sie können damit Funktionen als Parameter an andere Funktionen übergeben. T ist der Eingabetyp der Funktion, R ist der Rückgabetyp.

In Ihrem Beispiel müssen Sie eine Funktion übergeben, die Component als Eingabe verwendet und nichts zurückgibt - Void. In diesem Fall ist Function<T, R> nicht die beste Wahl, da kein Autoboxing vom Typ Void erfolgt. Die Schnittstelle, nach der Sie suchen, heißt Consumer<T> ( docs ) mit Methode 

void accept(T t);

Es würde so aussehen:

public void setAllComponents(Component[] myComponentArray, Consumer<Component> myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { 
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } 
        myMethod.accept(leaf);
    } 
}

Und Sie würden es mit Methodenverweisen aufrufen:

setAllComponents(this.getComponents(), this::changeColor);
setAllComponents(this.getComponents(), this::changeSize); 

Angenommen, Sie haben die changeColor () - und die ChangeSize () - Methode in derselben Klasse definiert.


Wenn Ihre Methode mehr als einen Parameter akzeptiert, können Sie BiFunction<T, U, R> - T und U als Eingabeparameter und R als Rückgabetyp verwenden. Es gibt auch BiConsumer<T, U> (zwei Argumente, kein Rückgabetyp). Für 3 und mehr Eingabeparameter müssen Sie leider selbst eine Schnittstelle erstellen. Zum Beispiel:

public interface Function4<A, B, C, D, R> {

    R apply(A a, B b, C c, D d);
}
6
JakubM

Verwenden Sie das Observer-Pattern (manchmal auch als Listener-Pattern bezeichnet):

interface ComponentDelegate {
    void doSomething(Component component);
}

public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) {
    // ...
    delegate.doSomething(leaf);
}

setAllComponents(this.getComponents(), new ComponentDelegate() {
                                            void doSomething(Component component) {
                                                changeColor(component); // or do directly what you want
                                            }
                                       });

new ComponentDelegate()... deklariert einen anonymen Typ, der die Schnittstelle implementiert.

1
EricSchaefer

Java hat einen Mechanismus, um den Namen zu übergeben und ihn aufzurufen. Es ist Teil des Reflektionsmechanismus . Ihre Funktion sollte zusätzliche Parameter der Klasse Method enthalten.

public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled)
{
...
Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist);
...
}
1
David Gruzman

Ich bin kein Java-Experte, aber ich löse Ihr Problem so:

@FunctionalInterface
public interface AutoCompleteCallable<T> {
  String call(T model) throws Exception;
}

Ich definiere den Parameter in meiner speziellen Schnittstelle

public <T> void initialize(List<T> entries, AutoCompleteCallable getSearchText) {.......
//call here
String value = getSearchText.call(item);
...
}

Schließlich implementiere ich getSearchText method während ich initialize method aufrufe.

initialize(getMessageContactModelList(), new AutoCompleteCallable() {
          @Override
          public String call(Object model) throws Exception {
            return "custom string" + ((xxxModel)model.getTitle());
          }
        })
0
Sercan özen

Ich glaube nicht, dass Lambdas dafür gedacht sind ... Java ist keine funktionale Programmiersprache und wird es auch nie sein, wir übergeben keine Methoden als Parameter. Denken Sie daran, dass Java objektorientiert ist, und in diesem Sinne können wir tun, was wir wollen. Die erste Idee ist, einfach ein "Objekt, das eine Methode enthält" als Parameter zu übergeben. Wenn Sie also die Methode "übergeben" müssen, übergeben Sie einfach eine Instanz dieser Klasse. Beachten Sie, dass Sie bei der Definition der Methode eine Instanz der Klasse, die die Methode enthält, als Parameter hinzufügen müssen. Dies sollte funktionieren, ist aber nicht das, was wir wollen, da Sie die Methode nur dann neu definieren können, wenn Sie Zugriff auf den Klassencode haben. Dies ist in vielen Fällen nicht möglich. Darüber hinaus denke ich, dass wenn jemand eine Methode als Parameter übergeben muss, dies darauf zurückzuführen ist, dass das Verhalten der Methode dynamisch sein sollte. Was ich damit meine, ist, dass der Programmierer, der Ihre Klassen verwendet, in der Lage sein sollte, zu bestimmen, was die Methode zurückgeben soll, nicht jedoch deren Typ. Glücklicherweise hat Java eine schöne und einfache Lösung: abstrakte Klassen. Abstrakte Klassen werden in einer Nussschale verwendet, wenn Sie die "Signatur" einer Methode kennen, aber nicht ihr Verhalten. Sie können den Namen und den Typ der Methode in eine abstrakte Klasse einschließen und eine Instanz dieser Klasse als Parameter für die Methode ... Warten Sie ... ist es nicht dasselbe wie zuvor? Und können Sie eine Instanz einer abstrakten Klasse haben? Nein und Nein ... aber auch Ja ... wenn Sie eine abstrakte Methode erstellen Sie MÜSSEN es auch in einer Klasse neu definieren, die die abstrakte Klasse erweitert, und Java wird aufgrund der dynamischen Bindung von Java (sofern Sie es nicht als statisch, privat und andere Dinge deklarieren) die neu definierte Version davon verwenden. Angenommen, wir möchten eine Funktion auf ein Zahlenarray anwenden: Wenn wir also ein Quadrat bilden wollen, sollten die Eingabe-Ausgabe so aussehen [1,2,3,4, ...] -> [1,4,9,16 , ...] (in einer funktionalen Programmiersprache wie haskell ist dies dank einiger Tools wie 'map', ...) ganz einfach. Beachten Sie, dass das Quadrieren von Zahlen nichts Besonderes ist t. Der Code sollte also so aussehen [args], f -> [f (args)]. Back to Java => Eine Funktion ist nur eine Methode. Wir wollen also eine Funktion, die eine andere Funktion auf ein Array anwendet. Kurz gesagt, wir müssen eine Methode als Parameter übergeben. So würde ich das machen ==>

1) Definieren Sie die WRAPPER-ABSTRACT-KLASSE UND DIE VERFAHREN

public  abstract class Function 
{
    public abstract double f(double x);
}

2) Definieren Sie die Klasse mit der Methode APPLY_TO_ARRAY 

public class ArrayMap 
{
public static double[] apply_to_array(double[] arr, Function fun)
{
    for(int i=0; i<arr.length;i++)
    {
        arr[i]=fun.f(arr[i]);
    }
    return arr;
}
}

3) ERSTELLE EINE TESTERKLASSE UND HABE EINEN SPASS

public class Testclass extends Function
{
    public static void main(String[] args) 
    {
        double[] myarr = {1,2,3,4};
        ArrayMap.apply_to_array(myarr, new Testclass());
        for (double k : myarr)
        {
        System.out.println(k);
        }

    }

    @Override
    public double f(double x) 
    {

        return Math.log(x);
    }   
}

Beachten Sie, dass wir ein Objekt vom Typ Function übergeben müssen. Da Testclass die Function-Klasse erweitert, können wir es verwenden. Daher erfolgt der Cast automatisch. 

0
omar kahol

Ich fand kein explizites Beispiel für die Verwendung von Java.util.function.Function für eine einfache Methode als Parameterfunktion. Hier ist ein einfaches Beispiel:

import Java.util.function.Function;

public class Foo {

  private Foo(String parameter) {
    System.out.println("I'm a Foo " + parameter);
  }

  public static Foo method(final String parameter) {
    return new Foo(parameter);
  }

  private static Function parametrisedMethod(Function<String, Foo> function) {
    return function;
  }

  public static void main(String[] args) {
    parametrisedMethod(Foo::method).apply("from a method");
  }
}

Grundsätzlich haben Sie ein Foo-Objekt mit einem Standardkonstruktor. Eine method, die als Parameter von der parametrisedMethod aufgerufen wird, die vom Typ Function<String, Foo> ist.

  • Function<String, Foo> bedeutet, dass die Funktion eine String als Parameter verwendet und eine Foo zurückgibt.
  • Der Foo::Method entspricht einem Lambda wie x -> Foo.method(x);
  • parametrisedMethod(Foo::method) könnte als x -> parametrisedMethod(Foo.method(x)) angesehen werden
  • Die .apply("from a method") ist im Grunde parametrisedMethod(Foo.method("from a method")) zu tun.

Was wird dann in der Ausgabe zurückgegeben:

>> I'm a Foo from a method

Das Beispiel sollte so ausgeführt werden, dass Sie kompliziertere Informationen aus den obigen Antworten mit verschiedenen Klassen und Schnittstellen ausprobieren können.

0
Sylhare

Hier ist ein grundlegendes Beispiel:

public class TestMethodPassing
{
    private static void println()
    {
        System.out.println("Do println");
    }

    private static void print()
    {
        System.out.print("Do print");
    }

    private static void performTask(BasicFunctionalInterface functionalInterface)
    {
        functionalInterface.performTask();
    }

    @FunctionalInterface
    interface BasicFunctionalInterface
    {
        void performTask();
    }

    public static void main(String[] arguments)
    {
        performTask(TestMethodPassing::println);
        performTask(TestMethodPassing::print);
    }
}

Ausgabe:

Do println
Do print
0
BullyWiiPlaza

Ich habe hier keine Lösung gefunden, die zeigt, wie eine Methode mit Parametern als Parameter einer Methode übergeben wird. Im Folgenden finden Sie ein Beispiel, wie Sie eine Methode mit bereits gebundenen Parameterwerten übergeben können. 

  1. Schritt 1: Erstellen Sie zwei Schnittstellen, eine mit Rückgabetyp, eine ohne. Java verfügt über ähnliche Schnittstellen, sie sind jedoch wenig praktisch, da sie das Auslösen von Ausnahmen nicht unterstützen.


    public interface Do {
    void run() throws Exception;
    }


    public interface Return {
        R run() throws Exception;
    }

  1. Beispiel, wie wir beide Schnittstellen verwenden, um den Methodenaufruf in der Transaktion einzuwickeln. Beachten Sie, dass wir die Methode mit Aktualparametern übergeben.


    //example - when passed method does not return any value
    public void tx(final Do func) throws Exception {
        connectionScope.beginTransaction();
        try {
            func.run();
            connectionScope.commit();
        } catch (Exception e) {
            connectionScope.rollback();
            throw e;
        } finally {
            connectionScope.close();
        }
    }

    //Invoke code above by 
    tx(() -> api.delete(6));

Ein anderes Beispiel zeigt, wie eine Methode übergeben wird, die tatsächlich etwas zurückgibt



        public  R tx(final Return func) throws Exception {
    R r=null;
    connectionScope.beginTransaction();
    try {
                r=func.run();
                connectionScope.commit();
            } catch (Exception e) {
                connectionScope.rollback();
                throw e;
            } finally {
                connectionScope.close();
            }
        return r;
        }
        //Invoke code above by 
        Object x= tx(() -> api.get(id));

0
Stan Sokolov