Exécutable avec un paramètre?

178

J'ai besoin d'un "Runnable qui accepte un paramètre" bien que je sache qu'un tel runnable n'existe pas vraiment.

Cela peut indiquer une faille fondamentale dans la conception de mon application et / ou un blocage mental dans mon cerveau fatigué, j'espère donc trouver ici quelques conseils sur la façon d'accomplir quelque chose comme ce qui suit, sans violer les principes fondamentaux d'OO:

  private Runnable mOneShotTask = new Runnable(String str) {
    public void run(String str) {
       someFunc(str);
    }
  };  

Une idée comment accomplir quelque chose comme ci-dessus?

uTubeFan
la source
7
Vous pouvez maintenant utiliser Consumer<T>.
Alex78191
1
J'ai lu les différentes réponses à cette question. Il me semble étrange que personne n'ait dit que vous pouvez ajouter à votre projet les exécutables dont vous avez besoin (avec un, deux, trois arguments ou plus) simplement en ajoutant une interface appropriée. J'ai créé un résumé commenté ici pour qui est intéressé: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Francesco Galgani
Ce n'est PAS un doublon de "Comment puis-je passer un paramètre à un thread Java". Et la réponse moderne est, comme @ Alex78191 dit: utilisationConsumer<T>
Epaga

Réponses:

228

Eh bien, cela fait presque 9 ans que j'ai publié ceci et pour être honnête, Java a apporté quelques améliorations depuis. Je vais laisser ma réponse originale ci-dessous, mais il n'est pas nécessaire que les gens fassent ce qu'il contient. Il y a 9 ans, lors de la révision du code, j'aurais demandé pourquoi ils l'avaient fait et peut-être l'avoir approuvé, peut-être pas. Avec les lambdas modernes disponibles, il est irresponsable d'avoir une réponse aussi votée recommandant une approche désuète (qui, en toute honnêteté, était douteuse au départ ...) Dans Java moderne, cette révision de code serait immédiatement rejetée, et ce serait suggéré:

void foo(final String str) {
    Thread t = new Thread(() -> someFunc(str));
    t.start();
}

Comme auparavant, des détails tels que la gestion de ce fil de manière significative sont laissés à l'exercice pour le lecteur. Mais pour le dire franchement, si vous avez peur d'utiliser des lambdas, vous devriez avoir encore plus peur des systèmes multithreads.

Réponse originale, juste parce que:

Vous pouvez déclarer une classe directement dans la méthode

void Foo(String str) {
    class OneShotTask implements Runnable {
        String str;
        OneShotTask(String s) { str = s; }
        public void run() {
            someFunc(str);
        }
    }
    Thread t = new Thread(new OneShotTask(str));
    t.start();
}
corsiKa
la source
Merci à tous! Toutes les solutions suggérées pointent vers la même approche mais je ne peux en accepter qu'une. Je dois être très fatigué de ne pas pouvoir trouver cela moi-même. +1 à tous.
uTubeFan
18
En fait, la plupart des gens ne savent pas que vous pouvez déclarer une classe dans une méthode. Certains considéreraient que c'est un style médiocre. Je suppose que c'est une question de goût. :)
corsiKa
3
interface publique ResultRunnable <T> {public void run (T result); }
Roman M
46

Vous pourriez le mettre dans une fonction.

String paramStr = "a parameter";
Runnable myRunnable = createRunnable(paramStr);

private Runnable createRunnable(final String paramStr){

    Runnable aRunnable = new Runnable(){
        public void run(){
            someFunc(paramStr);
        }
    };

    return aRunnable;

}

(Quand j'ai utilisé ceci, mon paramètre était un identifiant entier, que j'ai utilisé pour créer un hashmap de ID -> myRunnables. De cette façon, je peux utiliser le hashmap pour publier / supprimer différents objets myRunnable dans un gestionnaire.)

HaoQi Li
la source
3
Merci d'avoir partagé le code - j'aime quand les gens font ça au lieu de bavarder. Une question - L'approche ci-dessus est-elle correcte en matière de fuite de mémoire? Toutes les références que vous transmettez seront correctement éliminées?
nikib3ro
2
@ kape123 La réponse est "ça dépend". Tant qu'un Runnableobjet retourné par la méthode existe n'importe où, le paramStrne sera probablement pas éligible pour le garbage collection. Il est possible que si l'objet existe mais ne peut plus jamais être exécuté, le JIT (ou même javac) décide de le supprimer de la portée, mais nous ne devons pas nous fier à de telles optimisations car elles peuvent changer dans le futur.
corsiKa
"Merci d'avoir partagé le code - j'aime quand les gens font ça au lieu de bavarder." - quelle?
Rob Grant
30
theView.post(new Runnable() {
    String str;
    @Override                            
    public void run() {
        par.Log(str);                              
    }
    public Runnable init(String pstr) {
        this.str=pstr;
        return(this);
    }
}.init(str));

Créez une fonction init qui renvoie l'objet lui-même et initialisez les paramètres avec lui.

Tommi Rouvali
la source
1
Malheureusement, vous ne pourrez pas l'attribuer à une variable et appeler init ()
Andrew Glukhoff
11

J'utilise la classe suivante qui implémente l' interface Runnable . Avec cette classe, vous pouvez facilement créer de nouveaux threads avec des arguments

public abstract class RunnableArg implements Runnable {

    Object[] m_args;

    public RunnableArg() {
    }

    public void run(Object... args) {
        setArgs(args);
        run();
    }

    public void setArgs(Object... args) {
        m_args = args;
    }

    public int getArgCount() {
        return m_args == null ? 0 : m_args.length;
    }

    public Object[] getArgs() {
        return m_args;
    }
}
ubiquibacon
la source
2
Comment utiliseriez-vous cette classe abstraite pour exécuter un exécutable avec un paramètre?
EZDsIt
Je préfère utiliser le constructeur avec le (s) paramètre (s) public RunnableArg(Object... args) { setArgs(args); } puis décrire la classe locale: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } et l'utiliser Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz
10

Vous avez deux options:

  1. Définissez une classe nommée. Passez votre paramètre au constructeur de la classe nommée.

  2. Faites fermer votre classe anonyme sur votre "paramètre". Assurez-vous de le marquer comme final.

rlibby
la source
6

Depuis Java 8, la meilleure réponse est d'utiliser Consumer<T>:

https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html

C'est l'une des interfaces fonctionnelles, ce qui signifie que vous pouvez l'appeler comme une expression lambda:

void doSomething(Consumer<String> something) {
    something.accept("hello!");
}

...

doSomething( (something) -> System.out.println(something) )

...
Epaga
la source
5

Je voudrais d'abord savoir ce que vous essayez d'accomplir ici pour avoir besoin d'un argument à passer à new Runnable () ou à run (). La méthode habituelle devrait être d'avoir un objet Runnable qui passe des données (str) à ses threads en définissant des variables membres avant de démarrer. La méthode run () utilise ensuite ces valeurs de variable membre pour exécuter someFunc ()

atlantis
la source