2017-08-16 3 views
0

en Java et Scala, comment pourrais-je faire ce qui suit:Créer un paramètre qui est un Varier Fonction

Je veux être en mesure de passer une fonction dans une fonction comme paramètre où le paramètre de la fonction est un peu variable. Par exemple, ce que je suis enfermé dans maintenant en Java:

public void doSomething(Object object, Action1<Object> function) { 
    function.call(object); 
} 

public void doOtherThing(Object obj) { 
    System.out.println(obj); 
} 

doSomething("hello", this::doOtherThing); 

Voilà ce que je veux être en mesure de le faire:

public void doSomethingFancy(Object object, <Some Function Type Here> function) { 
    function.call(object); 
} 

public void doFancyThing(Object obj1, String str1, List list1) { 
    // do stuff 
} 
public void doFancyThing2(Object obj1, RandomObj rObj, Integer int1) { 
    // do stuff 
} 
... 
doSomething("hello", this::doFancyThing); 
doSomething("hello", this::doFancyThing2); 

Fondamentalement, je veux la fonction d'entrée pour un type de variable des paramètres d'entrée. ActionN ne fonctionnera pas car cela passerait un tableau d'objet dans ma méthode doFancyThing et cela ne compile évidemment pas.

+2

Créez une interface personnalisée. – shmosel

+0

Surcharger la signature de la méthode? avez-vous un ensemble spécifique de paramètres d'entrée? – drelliot

+0

Mise à jour de la question –

Répondre

0

Toute interface qui a exactement une méthode abstraite est une interface fonctionnelle, et peut donc être implémentée en utilisant une fonction lambda ou une référence de méthode.

@FunctionalInterface 
public class MyFancyInterface { 
    void call(Object obj1, String str1, List list1); 
} 
0

Que diriez-vous,

Une interface exécuteur:

public interface Executor<T> { 
    void execute(T source); 
} 

Un exécuteur testamentaire simple:

public class SimpleExecutor implements Executor<String> { 

    @Override 
    public void execute(String source) { 
     System.out.println(source); 
    } 

} 

Un exécuteur testamentaire de fantaisie:

public class FancyExecutor implements Executor<Object>{ 

    @Override 
    public void execute(Object source) { 
     System.out.println("doing some fancy stuff");   
    } 

} 

Une action:

public class Action { 

    public <T> void doSomething(T source, Executor<T> executor) { 
     executor.execute(source); 
    } 

} 

Et enfin:

public static void main(String [] args) { 
    Action action = new Action(); 
    action.doSomething("hey there", new SimpleExecutor()); 
    action.doSomething(new Object(), new FancyExecutor()); 
} 
0

Avec vos définitions, doSomethingFancy("hello", this::doFancyThing); (je suppose que vous vouliez ce lieu de doSomething("hello", this::doFancyThing); dans le second exemple) devrait appeler doFancyThing("hello"), ce qui est impossible: il est Il manque deux arguments! De même pour doSomethingFancy("hello", this::doFancyThing2);, qui appelle doFancyThing2("hello").

Et si vous y réfléchissez un peu, vous devriez constater que la modification de la définition de doSomethingFancy ne va pas aider: il n'y a tout simplement rien qu'il peut faire (sauf les choses banales comme le retour null, object, function.hashCode(), etc.). Donc, quand la langue ne vous permet même pas de le déclarer, c'est une bonne chose.

Deux possibilités réelles:

  1. fournir des arguments manquants dans le cadre de la fonction:

    public <T> void doSomething(T object, Action1<T> function) { 
        function.call(object); 
    } 
    
    doSomething("hello", x -> doFancyThing(x, "a", Collections.<Integer> emptyList())); 
    doSomething("hello", x -> doFancyThing2(x, some other arguments)); 
    

    Cela fonctionne et il fonctionne très bien.

  2. Utilisez varargs pour fournir tous les paramètres:

    public void doSomething(Action1<Object[]> function, Object... args) { 
        function.call(object); 
    } 
    
    doSomething(args -> doFancyThing((Object) args(0), (String) args(1), (List) args(2)), "hello", "a", Collections.<Integer> emptyList())); 
    

    Vous pouvez extraire la partie args -> doFancyThing((Object) args(0), (String) args(1), (List) args(2)) à une méthode générique, de sorte que les appels ressembleraient

    doSomething(toVarArgs(this::doFancyThing), "hello", "a", Collections.<Integer> emptyList())); 
    

Ceci est laissé un exercice.