209

I have a need for a "Runnable that accepts a parameter" although I know that such runnable doesn't really exist.

This may point to fundamental flaw in the design of my app and/or a mental block in my tired brain, so I am hoping to find here some advice on how to accomplish something like the following, without violating fundamental OO principles:

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

Any idea how to accomplish something like the above?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
uTubeFan
  • 6,664
  • 12
  • 41
  • 65
  • 21
    Now you can use `Consumer`. – Alex78191 Feb 25 '19 at 18:49
  • 2
    I've read the various answers to this question. It seems me strange that nobody told that you can add to your project the Runnables that you need (with one, two, three or more args) simply adding an appropriate interface. I created a commented gist here for who is interested: https://gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a – Francesco Galgani May 08 '19 at 13:07
  • This is NOT a duplicate to "How can I pass a parameter to a Java thread". And the modern answer is, like @Alex78191 says: use `Consumer` – Epaga Jan 09 '20 at 08:56
  • I usually create a class that implements run() and pass what ever data I need into the constructor or accesser/setter. – musterjunk Jan 05 '23 at 16:45
  • 1
    @Alex78191, can you further elaborate what you mean by use Consumer? – experiment unit 1998X Feb 07 '23 at 07:51
  • @experimentunit1998X https://stackoverflow.com/a/59660705/4854931 – Alex78191 Feb 07 '23 at 19:22

10 Answers10

257

Well it's been almost 9 years since I originally posted this and to be honest, Java has made a couple improvements since then. I'll leave my original answer below, but there's no need for people to do what is in it. 9 years ago, during code review I would have questioned why they did it and maybe approved it, maybe not. With modern lambdas available, it's irresponsible to have such a highly voted answer recommending an antiquated approach (that, in all fairness, was dubious to begin with...) In modern Java, that code review would be immediately rejected, and this would be suggested:

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

As before, details like handling that thread in a meaningful way is left as an exercise to the reader. But to put it bluntly, if you're afraid of using lambdas, you should be even more afraid of multi-threaded systems.

Original answer, just because:

You can declare a class right in the method

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
  • 81,495
  • 25
  • 153
  • 204
  • Thank you all! All of the suggested solution point to the same approach but I can only accept one. I must be very tired not being able to come up with this myself. +1 to all. – uTubeFan May 02 '11 at 04:09
  • 21
    Actually, most people don't know you can declare a class inside a method. Some would consider it to be poor style. I guess it's a matter of taste. :) – corsiKa May 02 '11 at 04:40
  • 3
    public interface ResultRunnable { public void run(T result); } – Roman M Aug 01 '18 at 00:22
  • I don't get the more modern approach. OK, there is no `Runnable` anymore. Any change to use it in a `Scheduler`? E.g. `Scheduling scheduling = new Scheduling(1); scheduling.startScheduler(new MyRunnable(), TimeUnit.DAYS, 1);` – BairDev May 26 '23 at 10:27
52

You could put it in a function.

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;

}

(When I used this, my parameter was an integer ID, which I used to make a hashmap of ID --> myRunnables. That way, I can use the hashmap to post/remove different myRunnable objects in a handler.)

HaoQi Li
  • 11,970
  • 14
  • 58
  • 77
  • 4
    Thanks for sharing the code - I love when people do that instead of just blabbering. One question - is above approach OK when it comes to Memory Leaking? All references you pass will be properly disposed of? – nikib3ro Jul 26 '12 at 07:11
  • 2
    @kape123 The answer is "it depends". As long as a `Runnable` object returned by the method exists anywhere, the `paramStr` will probably not be eligible for garbage collection. It is possible that if the object exists but can never be run again, the JIT (or even javac) may decide to remove it from scope, but we should not rely on such optimizations because they may change in the future. – corsiKa Apr 25 '13 at 22:27
35
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));

Create init function that returns object itself and initialize parameters with it.

Cairnarvon
  • 25,981
  • 9
  • 51
  • 65
Tommi Rouvali
  • 351
  • 3
  • 2
23

Since Java 8, the best answer is to use Consumer<T>:

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

It's one of the functional interfaces, which means you can call it as a lambda expression:

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

...

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

...
Epaga
  • 38,231
  • 58
  • 157
  • 245
  • If still using Runnable with lambda in Java 8, we can do as below: `void doSomething(String param) { CompletableFuture.runAsync(() -> howToDo(param));} private void howToDo(String param) { System.out.println(param);}` – Lampard Feb 04 '22 at 10:40
12

I use the following class which implements the Runnable interface. With this class you can easily create new threads with 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
  • 10,451
  • 28
  • 109
  • 179
  • 2
    How would you use this abstract class to run a runnable with a parameter? – EZDsIt Jun 07 '16 at 14:01
  • I prefer to use constructor with parameter(s) `public RunnableArg(Object... args) { setArgs(args); }` then describe local class: `class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } }` and use it `Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();` – GSD.Aaz Oct 12 '17 at 12:15
  • Perfect solution if you're developing for API < 24! I chose this solution over the Consumer solution, which is only for Java 8 (API >= 24). Thanks! – ONE Jan 25 '23 at 17:06
10

You have two options:

  1. Define a named class. Pass your parameter to the constructor of the named class.

  2. Have your anonymous class close over your "parameter". Be sure to mark it as final.

rlibby
  • 5,931
  • 20
  • 25
  • @Massa: see http://stackoverflow.com/questions/266806/is-there-any-performance-reason-to-declare-method-parameters-final-in-java – setzamora May 02 '11 at 04:53
5

I would first want to know what you are trying to accomplish here to need an argument to be passed to new Runnable() or to run(). The usual way should be to have a Runnable object which passes data(str) to its threads by setting member variables before starting. The run() method then uses these member variable values to do execute someFunc()

atlantis
  • 3,056
  • 9
  • 30
  • 41
1
/**
 * @author AbdelWadoud Rasmi
 * <p>
 * The goal of this class is to pass some parameters to a runnable instance, a good example is
 * after caching a file you need to pass the new path to user to do some work on it.
 */
public abstract class ParameterizedRunnable implements Runnable {
    private Object[] params;

    /**
     * @param params: parameters you want to pass the the runnable.
     */
    public ParameterizedRunnable(Object... params) {
        this.params = params;
    }

    /**
     * Code you want to run
     *
     * @param params:parameters you want to pass the the runnable.
     */
    protected abstract void run(Object... params);

    @Override
    public final void run() {
        run(params);
    }

    /**
     * setting params
     */
    public void setParams(Object... params) {
        this.params = params;
    }

    /**
     * getting params
     */
    public Object[] getParams() {
        return params;
    }
}
1

Best way so far:

Consumer<String> oneShot = str -> {
    
   somefunc(str);
    
};

oneShot.accept("myString");
Carlos López Marí
  • 1,432
  • 3
  • 18
  • 45
0

Create a new class that implements Runnable. In this class you can extend the run() method by making abstract methods of your choice.

public abstract class CCallback implements Runnable {
@Override
public void run() {}

public abstract void run(Object any);

}

Now you can use this class as a callback function like this:

public void whatever(CCallback callback) {
    String any = "any type of param";
    if (callback != null)
        callback.run(any);
}

"Whatever" method can now finish its task before our runnable runs:

whatever(new CCallback() {
    @Override
    public void run(Object any) {
        // Here you can reach "any"
    }
});
dot_cr2
  • 69
  • 1
  • 5