Запуск с параметром?

178

Мне нужен «Runnable, который принимает параметр», хотя я знаю, что такого runnable на самом деле не существует.

Это может указывать на фундаментальный недостаток в дизайне моего приложения и / или умственный блок в моем усталом мозге, поэтому я надеюсь найти здесь несколько советов о том, как сделать что-то вроде следующего, не нарушая фундаментальные принципы ОО:

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

Любая идея, как сделать что-то подобное выше?

uTubeFan
источник
7
Теперь вы можете использовать Consumer<T>.
Alex78191
1
Я прочитал различные ответы на этот вопрос. Мне кажется странным, что никто не сказал, что вы можете добавить в свой проект Runnables, которые вам нужны (с одним, двумя, тремя или более аргументами), просто добавив соответствующий интерфейс. Я создал комментарий здесь для тех, кто заинтересован: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Франческо
Это НЕ дубликат «Как я могу передать параметр в поток Java». И современный ответ, как @ Alex78191 говорит: использоватьConsumer<T>
Epaga

Ответы:

228

Прошло уже почти 9 лет с тех пор, как я опубликовал это, и, честно говоря, с тех пор Java сделала несколько улучшений. Я оставлю свой оригинальный ответ ниже, но людям не нужно делать то, что в нем. 9 лет назад, во время проверки кода, я бы спросил, почему они это сделали, и, возможно, одобрил, а может и нет. С доступными современными лямбдами, безответственно иметь такой высоко голосованный ответ, рекомендующий устаревший подход (который, честно говоря, был сомнительным для начала ...) В современной Java этот обзор кода был бы немедленно отклонен, и это было бы предложил:

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

Как и прежде, такие детали, как осмысленная обработка этой темы, оставлены читателю в качестве упражнения. Но, прямо скажем, если вы боитесь использовать лямбды, вам следует еще больше бояться многопоточных систем.

Оригинальный ответ, просто потому что:

Вы можете объявить класс прямо в методе

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
источник
Спасибо вам всем! Все предложенные решения указывают на один и тот же подход, но я могу принять только один. Я, должно быть, очень устал, не имея возможности придумать это сам. +1 ко всем.
uTubeFan
18
На самом деле, большинство людей не знают, что вы можете объявить класс внутри метода. Некоторые считают это плохим стилем. Я думаю, это вопрос вкуса. :)
CorsiKa
3
открытый интерфейс ResultRunnable <T> {public void run (T result); }
Роман М
46

Вы могли бы поместить это в функцию.

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;

}

(Когда я использовал это, моим параметром был целочисленный идентификатор, который я использовал для создания хэш-карты ID -> myRunnables. Таким образом, я могу использовать хэш-карту для публикации / удаления различных объектов myRunnable в обработчике.)

HaoQi Li
источник
3
Спасибо за то, что поделились кодом - мне нравится, когда люди делают это, а не просто болтают. Один вопрос - выше ли подход хорошо, когда дело доходит до утечки памяти? Все ссылки, которые вы передадите, будут правильно утилизированы?
nikib3ro
2
@ kape123 Ответ "это зависит". Пока Runnableобъект, возвращаемый методом, существует где-либо, paramStrвероятно, он не будет иметь права на сборку мусора. Возможно, что если объект существует, но никогда не сможет быть запущен снова, JIT (или даже javac) может решить удалить его из области, но мы не должны полагаться на такие оптимизации, потому что они могут измениться в будущем.
corsiKa
«Спасибо за то, что поделились кодом - мне нравится, когда люди делают это, а не просто болтают». - какие?
Роб Грант
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));

Создать функцию инициализации, которая возвращает сам объект и инициализировать параметры с ним.

Томми Рували
источник
1
К сожалению, вы не сможете присвоить его переменной и вызвать init ()
Эндрю Глухофф,
11

Я использую следующий класс, который реализует интерфейс Runnable . С помощью этого класса вы можете легко создавать новые темы с аргументами

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
источник
2
Как бы вы использовали этот абстрактный класс для запуска runnable с параметром?
EZDsIt
Я предпочитаю использовать конструктор с параметром (ами), а public RunnableArg(Object... args) { setArgs(args); } затем описывать локальный класс: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } и использовать его Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz
10

У вас есть два варианта:

  1. Определить именованный класс. Передайте ваш параметр конструктору именованного класса.

  2. Ваш анонимный класс должен быть закрыт по вашему «параметру». Обязательно отметьте это как final.

rlibby
источник
6

Начиная с Java 8, лучший ответ - использовать Consumer<T>:

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

Это один из функциональных интерфейсов, что означает, что вы можете назвать его как лямбда-выражение:

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

...

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

...
Epaga
источник
5

Сначала я хотел бы знать, что вы пытаетесь выполнить, чтобы аргумент передавался в новый Runnable () или run (). Обычным способом должно быть наличие объекта Runnable, который передает данные (str) своим потокам, устанавливая переменные-члены перед запуском. Затем метод run () использует значения переменных-членов для выполнения someFunc ().

Атлантида
источник