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

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

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

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

Есть идеи, как сделать что-то подобное?


person uTubeFan    schedule 02.05.2011    source источник
comment
Теперь вы можете использовать Consumer<T>.   -  person Alex78191    schedule 25.02.2019
comment
Я читал разные ответы на этот вопрос. Мне кажется странным, что никто не сказал, что вы можете добавить в свой проект необходимые вам Runnables (с одним, двумя, тремя или более аргументами), просто добавив соответствующий интерфейс. Я создал здесь комментарий для тех, кто заинтересован: gist.github.com/jsfan3/3a66fad733e3   -  person Francesco Galgani    schedule 08.05.2019
comment
Это НЕ дубликат раздела Как передать параметр в поток Java. И современный ответ такой: @ Alex78191 говорит: используйте Consumer<T>   -  person Epaga    schedule 09.01.2020


Ответы (7)


Что ж, прошло почти 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();
}
person corsiKa    schedule 02.05.2011
comment
Спасибо вам всем! Все предлагаемые решения указывают на один и тот же подход, но я могу согласиться только с одним. Я, должно быть, очень устал от того, что сам не могу это придумать. +1 ко всем. - person uTubeFan; 02.05.2011
comment
На самом деле, большинство людей не знают, что можно объявить класс внутри метода. Некоторые сочтут это плохим стилем. Думаю, дело вкуса. :) - person corsiKa; 02.05.2011
comment
общедоступный интерфейс ResultRunnable ‹T› {общедоступный пробег (результат T); } - person Roman M; 01.08.2018

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

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 в обработчике.)

person HaoQi Li    schedule 19.04.2012
comment
Спасибо, что поделились кодом - мне нравится, когда люди делают это, а не просто болтают. Один вопрос - подходит ли вышеуказанный подход, когда дело доходит до утечки памяти? Все ссылки, которые вы передаете, будут должным образом уничтожены? - person nikib3ro; 26.07.2012
comment
@ kape123 Ответ зависит от обстоятельств. Пока объект Runnable, возвращаемый методом, существует где-либо, paramStr, вероятно, не будет иметь права на сборку мусора. Возможно, что если объект существует, но никогда не может быть запущен снова, JIT (или даже javac) может решить удалить его из области видимости, но мы не должны полагаться на такие оптимизации, потому что они могут измениться в будущем. - person corsiKa; 26.04.2013
comment
Спасибо, что поделились кодом - мне нравится, когда люди делают это, а не просто болтают. - Какие? - person Rob Grant; 05.11.2018
comment
@RobGrant Настоящая ирония заключается в том, что после этого они специально просят эту болтовню !! О, они хотели только болтовни, в которой они нуждались? понятно. - person corsiKa; 19.10.2020

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

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

person Tommi Rouvali    schedule 23.05.2013
comment
К сожалению, вы не сможете присвоить его переменной и вызвать init () - person Andrew Glukhoff; 08.08.2018

Я использую следующий класс, который реализует интерфейс 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;
    }
}
person ubiquibacon    schedule 30.01.2014
comment
Как бы вы использовали этот абстрактный класс для запуска исполняемого файла с параметром? - person EZDsIt; 07.06.2016
comment
Я предпочитаю использовать конструктор с параметром (ами) 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(); - person GSD.Aaz; 12.10.2017

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

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

  2. Сделайте так, чтобы ваш анонимный класс закрыл ваш «параметр». Обязательно пометьте его как final.

person rlibby    schedule 02.05.2011
comment
@Massa: см. stackoverflow.com/questions/266806/ - person setzamora; 02.05.2011

Начиная с 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) )

...
person Epaga    schedule 09.01.2020

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

person atlantis    schedule 02.05.2011