Działa z parametrem?

178

Potrzebuję „Runnable, który akceptuje parametr”, chociaż wiem, że taki runnable tak naprawdę nie istnieje.

Może to wskazywać na fundamentalną wadę w projekcie mojej aplikacji i / lub blokadę umysłową w moim zmęczonym mózgu, więc mam nadzieję, że znajdę tutaj kilka porad, jak osiągnąć coś takiego, jak poniżej, bez naruszania podstawowych zasad OO:

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

Masz pomysł, jak osiągnąć coś podobnego do powyższego?

uTubeFan
źródło
7
Teraz możesz użyć Consumer<T>.
Alex78191
1
Przeczytałem różne odpowiedzi na to pytanie. Wydaje mi się dziwne, że nikt nie powiedział, że możesz dodać do swojego projektu Runnable, których potrzebujesz (z jednym, dwoma, trzema lub więcej argumentami), po prostu dodając odpowiedni interfejs. Stworzyłem tutaj skomentowaną treść dla zainteresowanych: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Francesco Galgani
NIE jest to duplikat artykułu „Jak mogę przekazać parametr do wątku Java”. A współczesna odpowiedź brzmi, jak @ Alex78191 mówi: użyjConsumer<T>
Epaga

Odpowiedzi:

228

Cóż, minęło prawie 9 lat, odkąd pierwotnie to opublikowałem i szczerze mówiąc, od tego czasu Java wprowadziła kilka ulepszeń. Zostawię moją pierwotną odpowiedź poniżej, ale ludzie nie muszą robić tego, co w niej jest. 9 lat temu, podczas przeglądu kodu, zapytałbym, dlaczego to zrobili i może to zaakceptowali, a może nie. Przy dostępnych nowoczesnych lambdach nieodpowiedzialne jest posiadanie tak wysoko ocenianej odpowiedzi zalecającej przestarzałe podejście (które, szczerze mówiąc, było wątpliwe na początku ...) We współczesnej Javie ten przegląd kodu zostałby natychmiast odrzucony, a to byłoby zasugerował:

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

Tak jak poprzednio, szczegóły, takie jak obsługa tego wątku w znaczący sposób, pozostawia się czytelnikowi jako ćwiczenie. Ale mówiąc wprost, jeśli boisz się używania lambd, jeszcze bardziej powinieneś bać się systemów wielowątkowych.

Oryginalna odpowiedź, tylko dlatego, że:

Możesz zadeklarować klasę bezpośrednio w metodzie

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
źródło
Dziękuję wam wszystkim! Wszystkie sugerowane rozwiązania wskazują na to samo podejście, ale mogę zaakceptować tylko jedno. Muszę być bardzo zmęczony, nie mogąc sam tego wymyślić. +1 dla wszystkich.
uTubeFan
18
W rzeczywistości większość ludzi nie wie, że można zadeklarować klasę wewnątrz metody. Niektórzy uznaliby to za kiepski styl. Myślę, że to kwestia gustu. :)
corsiKa
3
publiczny interfejs ResultRunnable <T> {public void run (wynik T); }
Roman M
46

Możesz umieścić to w funkcji.

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;

}

(Kiedy tego użyłem, moim parametrem był identyfikator całkowity, którego użyłem do utworzenia skrótu ID -> myRunnables. W ten sposób mogę użyć hashmap do opublikowania / usunięcia różnych obiektów myRunnable w module obsługi.)

HaoQi Li
źródło
3
Dzięki za udostępnienie kodu - uwielbiam, gdy ludzie to robią, zamiast tylko gadać. Jedno pytanie - czy powyższe podejście jest OK, jeśli chodzi o wyciek pamięci? Wszystkie przekazane referencje zostaną odpowiednio usunięte?
nikib3ro
2
@ kape123 Odpowiedź brzmi: „to zależy”. Dopóki Runnableobiekt zwrócony przez metodę istnieje gdziekolwiek, paramStrprawdopodobnie nie będzie on kwalifikował się do czyszczenia pamięci. Jest możliwe, że jeśli obiekt istnieje, ale nie można go już nigdy uruchomić, JIT (lub nawet javac) może zdecydować o usunięciu go z zakresu, ale nie powinniśmy polegać na takich optymalizacjach, ponieważ mogą się one zmienić w przyszłości.
corsiKa
„Dzięki za udostępnienie kodu - uwielbiam, kiedy ludzie to robią, zamiast tylko gadać”. - co?
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));

Utwórz funkcję init, która zwraca sam obiekt i inicjalizuje z nim parametry.

Tommi Rouvali
źródło
1
Niestety nie będziesz w stanie przypisać jej do zmiennej i wywołać init ()
Andrew Glukhoff
11

Używam następującej klasy, która implementuje interfejs Runnable . Dzięki tej klasie możesz łatwo tworzyć nowe wątki z argumentami

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
źródło
2
Jak użyłbyś tej klasy abstrakcyjnej do uruchomienia runnable z parametrem?
EZDsIt
Wolę użyć konstruktora z parametrami, a public RunnableArg(Object... args) { setArgs(args); } następnie opisać lokalną klasę: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } i użyć jej Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz
10

Masz dwie możliwości:

  1. Zdefiniuj nazwaną klasę. Przekaż parametr do konstruktora nazwanej klasy.

  2. Zamknij klasę anonimową nad "parametrem". Pamiętaj, aby oznaczyć go jako final.

rlibby
źródło
@Massa: patrz stackoverflow.com/questions/266806/ ...
setzamora
6

Od wersji Java 8 najlepszą odpowiedzią jest użycie Consumer<T>:

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

Jest to jeden z funkcjonalnych interfejsów, co oznacza, że ​​można go nazwać wyrażeniem lambda:

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

...

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

...
Epaga
źródło
5

Najpierw chciałbym wiedzieć, co próbujesz tutaj osiągnąć, aby potrzebować argumentu do przekazania do new Runnable () lub do run (). Zwykłym sposobem powinno być posiadanie obiektu Runnable, który przekazuje dane (str) do swoich wątków przez ustawienie zmiennych składowych przed rozpoczęciem. Następnie metoda run () używa tych wartości zmiennych składowych do wykonania funkcji someFunc ()

Atlantyda
źródło