Java Pass-methode als parameter

Ik ben op zoek naar een manier om een ​​methode door te geven op verwijzing. Ik begrijp dat Java geen methoden als parameters passeert, maar ik zou graag een alternatief willen krijgen.

Ik heb de interfaces verteld, zijn het alternatief voor het passeren van methoden als parameters, maar ik begrijp niet hoe een interface als een methode kan handelen door middel van verwijzing. Als ik het goed begrijp, is een interface gewoon een abstracte reeks methoden die niet worden gedefinieerd. Ik wil geen interface verzenden die elke keer moet worden gedefinieerd omdat verschillende methoden dezelfde methode kunnen bellen met dezelfde parameters.

Wat ik zou willen bereiken is iets vergelijkbaars met dit:

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
}

opgeroepen, zoals:

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

Antwoord 1, Autoriteit 100%

Bewerken : vanaf Java 8, Lambda Expressions zijn een leuke oplossing als andere Antwoorden hebben erop gewezen. Het onderstaande antwoord is geschreven voor Java 7 en eerder …


Bekijk het opdrachtpatroon.

// 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");
    }
}

Bewerken:zoals Pete Kirkham aangeeft, is er een andere manier om dit te doen met een Bezoeker. De bezoekersaanpak is iets ingewikkelder – uw knooppunten moeten allemaal bezoekersbewust zijn met een acceptVisitor()-methode – maar als u een complexere objectgrafiek moet doorlopen, is het de moeite waard om dit te onderzoeken.


Antwoord 2, autoriteit 37%

In Java 8 kunt u nu gemakkelijker een methode doorgeven met behulp van lambda Uitdrukkingen en methode referenties. Eerst, wat achtergrond: een functionele interface is een interface met één en slechts één abstracte methode, hoewel het een willekeurig aantal Standaardmethoden (nieuw in Java 8) en statische methoden. Een Lambda-expressie kan de abstracte methode snel implementeren, zonder dat alle onnodige syntaxis nodig is als u geen lambda-expressie gebruikt.

Zonder Lambda-uitdrukkingen:

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

Met Lambda Expressions:

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

Hier is een fragment van de Java-tutorial op Lambda Expressions :

Syntaxis van Lambda Expressions

Een lambda-expressie bestaat uit het volgende:

  • Een door komma’s gescheiden lijst van formele parameters die tussen haakjes zijn ingesloten. De checkperson.Test-methode bevat één parameter, p,
    die een instantie van de persoonsklasse vertegenwoordigt.

    Opmerking : You
    kan het gegevenstype van de parameters in een lambda-expressie weglaten. In
    Bovendien kunt u de haakjes weglaten als er slechts één parameter is.
    De volgende Lambda-expressie is bijvoorbeeld ook geldig:

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

  • Een hoofdtekst, die bestaat uit een enkele expressie of een instructieblok. In dit voorbeeld wordt de volgende uitdrukking gebruikt:

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

    Als u één expressie opgeeft, evalueert de Java-runtime de expressie en retourneert vervolgens de waarde ervan. Alternatief,
    u kunt een retourverklaring gebruiken:

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

    Een return-statement is geen expressie; in een lambda-expressie moet u uitspraken tussen accolades ({}) plaatsen. U heeft echter geen
    om een aanroep van een ongeldige methode tussen accolades te plaatsen. Bijvoorbeeld de
    het volgende is een geldige lambda-expressie:

    email -> System.out.println(email)
    

Merk op dat een lambda-expressie veel lijkt op een methodedeclaratie;
je kunt lambda-expressies beschouwen als anonieme methoden—methoden
zonder naam.


Hier ziet u hoe u “een methode kunt doorgeven” met een lambda-expressie:

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));
    }
}

Klasse Ckan zelfs een beetje verder worden verkort door het gebruik van methode-referenties zoals SO:

class C {
    A a = new A();
    B b = new B();
    public C() {
        b.setAllComponents(this.getComponents(), a::changeColor);
        b.setAllComponents(this.getComponents(), a::changeSize);
    }
}

Antwoord 3, Autoriteit 17%

Sinds Java 8 is er een Function<T, R>interface (docs ), die methode

heeft

R apply(T t);

U kunt het gebruiken om functies als parameters naar andere functies te geven. T is het invoertype van de functie, R is het retourtype.

In uw voorbeeld moet u een functie doorgeven die het type Componentals invoer gebruikt en niets retourneert – Void. In dit geval is Function<T, R>niet de beste keuze, aangezien er geen autoboxing van het type Void is. De interface die u zoekt heet Consumer<T>(docs) met methode

void accept(T t);

Het zou er zo uitzien:

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);
    } 
}

En je zou het kunnen noemen met behulp van methodeverwijzingen:

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

Ervan uitgaande dat u de methoden changeColor() en changeSize() in dezelfde klasse hebt gedefinieerd.


Als uw methode meer dan één parameter accepteert, kunt u BiFunction<T, U, R>gebruiken – T en U zijn typen invoerparameters en R is retourtype. Er is ook BiConsumer<T, U>(twee argumenten, geen retourtype). Helaas moet u voor 3 en meer invoerparameters zelf een interface maken. Bijvoorbeeld:

public interface Function4<A, B, C, D, R> {
    R apply(A a, B b, C c, D d);
}

Antwoord 4, autoriteit 12%

Gebruik het java.lang.reflect.Methodobject en roep invoke

aan


Antwoord 5, autoriteit 8%

Definieer eerst een interface met de methode die u als parameter wilt doorgeven

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

Implementeer een klasse met de methode

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

// Zo aanroepen

Callable cmd = new Test();

Hiermee kunt u cmd als parameter doorgeven en de methodeaanroep aanroepen die in de interface is gedefinieerd

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

Antwoord 6, autoriteit 7%

Hoewel dit nog niet geldt voor Java 7 en lager, denk ik dat we naar de toekomst moeten kijken en in ieder geval de wijzigingenin nieuwe versies zoals Java 8.

Deze nieuwe versie brengt namelijk lambda’sen methodeverwijzingen naar Java (samen met nieuwe API’s, die een andere geldige oplossing voor dit probleem zijn. Hoewel ze nog steeds een interface nodig hebben, is er geen nieuwe objecten worden gemaakt en extra klassenbestanden hoeven de uitvoermappen niet te vervuilen vanwege de verschillende behandeling door de JVM.

Zowel smaken (Lambda en methode-referentie) vereisen een interface die beschikbaar is met een enkele methode waarvan de handtekening wordt gebruikt:

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

Namen van methoden zullen er niet toe doen van hier. Waar een lambda wordt geaccepteerd, is ook een methode-referentie. Om hier bijvoorbeeld onze handtekening te gebruiken:

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

Dit is slechts een simpele interface-aanwijzing, omhoog totdat de lambda 1 wordt aangenomen:

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

Dit wordt uitgevoerd:

Lambda reached!
lambda return

Methode-referenties zijn vergelijkbaar. Gegeven:

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

en main:

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

de uitvoer zou real static methodzijn. Methodeverwijzingen kunnen statisch zijn, bijvoorbeeld, niet-statisch met willekeurige instanties en zelfs constructeurs. Voor de constructor zou iets vergelijkbaars met ClassName::newworden gebruikt.

1Dit wordt door sommigen niet als een lambda beschouwd, omdat het bijwerkingen heeft. Het illustreert echter wel het gebruik ervan op een meer eenvoudig te visualiseren manier.


Antwoord 7, autoriteit 5%

De laatste keer dat ik het controleerde, is Java niet in staat om te doen wat je wilt; je moet ‘work-arounds’ gebruiken om dergelijke beperkingen te omzeilen. Voor zover ik het zie, ZIJN interfaces een alternatief, maar geen goed alternatief. Misschien bedoelde degene die je dat heeft verteld zoiets als dit:

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
}

Waarmee je vervolgens een beroep op zou doen:

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

Antwoord 8, Autoriteit 4%

Als u deze methoden niet nodig hebt om iets terug te keren, kunt u ze rendelbare objecten retourneren.

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

Gebruik het dan zoals:

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

Antwoord 9, Autoriteit 2%

Ik heb geen expliciet genoeg gevonden voor mij over het gebruik van java.util.function.Functionvoor eenvoudige methode als parameterfunctie. Hier is een eenvoudig voorbeeld:

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");
  }
}

In principe heb je een FooObject met een standaardconstructeur. A methoddie wordt genoemd als een parameter uit de parametrisedMethoddie van het type Function<String, Foo>.

  • Function<String, Foo>betekent dat de functie een Stringals parameter neemt en een Fooretourneert.
  • De Foo::Methodkomt overeen met een lambda zoals x -> Foo.method(x);
  • parametrisedMethod(Foo::method)kan worden gezien als x -> parametrisedMethod(Foo.method(x))
  • De .apply("from a method")is in feite om te doen parametrisedMethod(Foo.method("from a method"))

die vervolgens in de uitgang terugkeren:

>> I'm a Foo from a method

Het voorbeeld moet worden uitgevoerd, zoals het is, u kunt dan meer gecompliceerde dingen uit de bovenstaande antwoorden met verschillende klassen en interfaces proberen.


Antwoord 10

Java heeft een mechanisme om naam te geven en noem het. Het maakt deel uit van het reflectiemechanisme.
Uw functie moet aanvullende parameter van klassenmethode gebruiken.

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

Antwoord 11

Gebruik het Observer-patroon (soms ook wel luisterpatroon genoemd):

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()...declareert een anoniem type dat de interface implementeert.


Antwoord 12

Hier is een eenvoudig voorbeeld:

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);
    }
}

Uitvoer:

Do println
Do print

Antwoord 13

Ik ben geen Java-expert, maar ik los uw probleem als volgt op:

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

Ik definieer de parameter in mijn speciale interface

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

Ten slotte implementeer ik de getSearchText-methode terwijl ik de initialize-methode aanroep.

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

Antwoord 14

Ik heb hier geen oplossing gevonden die laat zien hoe een methode moet worden doorgegeven met parameters die eraan zijn gebonden als een parameter van een methode. Bellow is een voorbeeld van hoe u een methode kunt doorgeven met parameterwaarden die er al aan zijn gebonden.

  1. Stap 1: Maak twee interfaces, één met retourtype en één zonder. Java heeft vergelijkbare interfaces, maar ze zijn van weinig praktisch nut omdat ze Exception throwing niet ondersteunen.

    public interface Do {
    void run() throws Exception;
    }
    public interface Return {
        R run() throws Exception;
    }
  1. Voorbeeld van hoe we beide interfaces gebruiken om de methodeaanroep in een transactie in te pakken. Merk op dat we de methode doorgeven met werkelijke parameters.

    //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));

Een ander voorbeeld laat zien hoe je een methode doorgeeft die daadwerkelijk iets teruggeeft


        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));

Antwoord 15

Voorbeeld van oplossing met reflectie, doorgegeven methode moet openbaar zijn

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class Program {
    int i;
    public static void main(String[] args) {
        Program   obj = new Program();    //some object
        try {
            Method method = obj.getClass().getMethod("target");
            repeatMethod( 5, obj, method );
        } 
        catch ( NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            System.out.println( e ); 
        }
    }
    static void repeatMethod (int times, Object object, Method method)
        throws IllegalAccessException, InvocationTargetException {
        for (int i=0; i<times; i++)
            method.invoke(object);
    }
    public void target() {                 //public is necessary
        System.out.println("target(): "+ ++i);
    }
}

Antwoord 16

Ik waardeer de bovenstaande antwoorden, maar ik kon hetzelfde gedrag bereiken met de onderstaande methode; een idee geleend van Javascript-callbacks. Ik sta open voor correctie, maar tot nu toe zo goed (in productie).

Het idee is om het retourtype van de functie in de handtekening te gebruiken, wat betekent dat de opbrengst statisch moet zijn.

Hieronder staat een functie die een proces uitvoert met een time-out.

public static void timeoutFunction(String fnReturnVal) {
    Object p = null; // whatever object you need here
    String threadSleeptime = null;
    Config config;
    try {
        config = ConfigReader.getConfigProperties();
        threadSleeptime = config.getThreadSleepTime();
    } catch (Exception e) {
        log.error(e);
        log.error("");
        log.error("Defaulting thread sleep time to 105000 miliseconds.");
        log.error("");
        threadSleeptime = "100000";
    }
    ExecutorService executor = Executors.newCachedThreadPool();
    Callable<Object> task = new Callable<Object>() {
        public Object call() {
            // Do job here using --- fnReturnVal --- and return appropriate value
            return null;
        }
    };
    Future<Object> future = executor.submit(task);
    try {
        p = future.get(Integer.parseInt(threadSleeptime), TimeUnit.MILLISECONDS);
    } catch (Exception e) {
        log.error(e + ". The function timed out after [" + threadSleeptime
                + "] miliseconds before a response was received.");
    } finally {
        // if task has started then don't stop it
        future.cancel(false);
    }
}
private static String returnString() {
    return "hello";
}
public static void main(String[] args) {
    timeoutFunction(returnString());
}

Other episodes