Виконується з параметром?


178

У мене є потреба у "Runnable, який приймає параметр", хоча я знаю, що такого запуску насправді не існує.

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

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

Будь-яка ідея, як здійснити щось подібне?


7
Тепер ви можете використовувати Consumer<T>.
Alex78191

1
Я прочитав різні відповіді на це питання. Мені здається дивним, що ніхто не сказав, що ви можете додати до свого проекту ті Runnables, які вам потрібні (з одним, двома, трьома і більше аргументами), просто додавши відповідний інтерфейс. Я створив тут коментовану історію
Francesco Galgani

Це НЕ дублікат "Як я можу передати параметр потоку Java". І сучасна відповідь, як @ Alex78191 каже: використовуйтеConsumer<T>
Епага

Відповіді:


228

Ну, пройшло майже 9 років, як я спочатку опублікував це і, якщо чесно, Java з тих пір зробила пару вдосконалень. Я залишу свою оригінальну відповідь нижче, але людям не потрібно робити те, що в ній. 9 років тому під час перегляду коду я б запитав, чому вони це зробили, і, можливо, це затвердили, а може, і ні. За наявних сучасних лямбдаз безвідповідально мати таку високоголосну відповідь, що рекомендує антикваріатний підхід (який, справедливо кажучи, був сумнівним для початку ...) У сучасній Яві цей огляд коду буде негайно відхилено, і це було б запропонував:

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

Дякую вам всім! Всі запропоновані рішення вказують на той самий підхід, але я можу прийняти лише один. Я, мабуть, дуже втомився, не можу сам придумати це. +1 для всіх.
uTubeFan

18
Насправді, більшість людей не знає, що ви можете оголосити клас всередині методу. Деякі вважають це поганим стилем. Я думаю, це питання смаку. :)
corsiKa

3
публічний інтерфейс ResultRunnable <T> {public void run (T результат); }
Роман М

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;

}

(Коли я використовував це, мій параметр був цілим ідентифікатором, який я використовував для створення хешмапу ідентифікатора -> myRunnables. Таким чином, я можу використовувати хешмап для розміщення / видалення різних об єктів myRunnable у обробнику.)


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

Створіть функцію init, яка повертає сам об'єкт, і ініціалізуйте параметри з ним.


1
На жаль, ви не зможете призначити його змінній та викликати init ()
Andrew Glukhoff

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

2
Як би ви використовували цей абстрактний клас для запуску запуску з параметром?
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.



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

...

5

Я спершу хотів би знати, що ви намагаєтеся зробити тут, щоб знадобитися аргумент, який потрібно передати новому Runnable () або run (). Звичайним способом має бути об’єкт Runnable, який передає дані (str) у свої потоки, встановлюючи змінні члена перед запуском. Потім метод run () використовує ці значення змінних членів для виконання someFunc ()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.