Dapat dijalankan dengan parameter?

178

Saya memiliki kebutuhan untuk "Runnable yang menerima parameter" walaupun saya tahu bahwa runnable tidak benar-benar ada.

Ini mungkin menunjukkan kelemahan mendasar dalam desain aplikasi saya dan / atau blok mental di otak saya yang lelah, jadi saya berharap untuk menemukan beberapa saran di sini tentang cara mencapai sesuatu seperti berikut, tanpa melanggar prinsip-prinsip OO mendasar:

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

Adakah cara untuk mencapai sesuatu seperti di atas?

uTubeFan
sumber
7
Sekarang kamu bisa menggunakannya Consumer<T>.
Alex78191
1
Saya telah membaca berbagai jawaban untuk pertanyaan ini. Sepertinya saya aneh bahwa tidak ada yang mengatakan bahwa Anda dapat menambah proyek Anda Runnables yang Anda butuhkan (dengan satu, dua, tiga atau lebih argumen) hanya menambahkan antarmuka yang sesuai. Saya membuat intisari komentar di sini untuk siapa yang tertarik: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Francesco Galgani
Ini BUKAN duplikat untuk "Bagaimana saya bisa melewatkan parameter ke thread Java". Dan jawaban modernnya adalah, seperti @ Alex78191 mengatakan: useConsumer<T>
Epaga

Jawaban:

228

Sudah hampir 9 tahun sejak saya memposting ini dan sejujurnya, Java telah membuat beberapa perbaikan sejak itu. Saya akan meninggalkan jawaban asli saya di bawah, tetapi tidak perlu bagi orang untuk melakukan apa yang ada di dalamnya. 9 tahun yang lalu, selama tinjauan kode saya akan mempertanyakan mengapa mereka melakukannya dan mungkin menyetujuinya, mungkin tidak. Dengan lambda modern tersedia, itu tidak bertanggung jawab untuk memiliki jawaban yang sangat tinggi merekomendasikan pendekatan kuno (yang, pada semua keadilan, meragukan untuk memulai dengan ...) Di Jawa modern, bahwa tinjauan kode akan segera ditolak, dan ini akan menjadi menyarankan:

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

Seperti sebelumnya, detail seperti menangani utas itu dengan cara yang bermanfaat dibiarkan sebagai latihan bagi pembaca. Tapi terus terang, jika Anda takut menggunakan lambdas, Anda harus lebih takut dengan sistem multi-threaded.

Jawaban asli, hanya karena:

Anda bisa mendeklarasikan kelas tepat di metode

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
sumber
Terima kasih semua! Semua solusi yang disarankan menunjuk ke pendekatan yang sama tetapi saya hanya bisa menerimanya. Saya pasti sangat lelah karena tidak bisa membuat ini sendiri. +1 untuk semua.
uTubeFan
18
Sebenarnya, kebanyakan orang tidak tahu Anda bisa mendeklarasikan kelas di dalam suatu metode. Beberapa orang akan menganggapnya sebagai gaya yang buruk. Saya kira ini masalah selera. :)
corsiKa
3
antarmuka publik ResultRunnable <T> {public void run (hasil T); }
Roman M
46

Anda bisa memasukkannya ke dalam fungsi.

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;

}

(Ketika saya menggunakan ini, parameter saya adalah ID integer, yang saya gunakan untuk membuat hashmap ID -> myRunnables. Dengan begitu, saya bisa menggunakan hashmap untuk memposting / menghapus objek myRunnable yang berbeda di dalam handler.)

HaoQi Li
sumber
3
Terima kasih telah berbagi kode - Saya suka ketika orang melakukannya bukan hanya mengoceh. Satu pertanyaan - apakah pendekatan di atas OK ketika datang ke Memory Leaking? Semua referensi yang Anda lewati akan dibuang dengan benar?
nikib3ro
2
@ kape123 Jawabannya adalah "itu tergantung". Selama Runnableobjek yang dikembalikan oleh metode ada di mana saja, paramStrmungkin tidak akan memenuhi syarat untuk pengumpulan sampah. Ada kemungkinan bahwa jika objek ada tetapi tidak pernah dapat dijalankan lagi, JIT (atau bahkan javac) dapat memutuskan untuk menghapusnya dari ruang lingkup, tetapi kita tidak boleh mengandalkan optimisasi tersebut karena mereka dapat berubah di masa depan.
corsiKa
"Terima kasih telah berbagi kode - saya suka ketika orang melakukan itu, bukan hanya mengoceh." - apa?
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));

Buat fungsi init yang mengembalikan objek itu sendiri dan menginisialisasi parameter dengannya.

Tommi Rouvali
sumber
1
Sayangnya Anda tidak akan dapat menetapkannya ke variabel dan memanggil init ()
Andrew Glukhoff
11

Saya menggunakan kelas berikut yang mengimplementasikan antarmuka Runnable . Dengan kelas ini Anda dapat dengan mudah membuat utas baru dengan argumen

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
sumber
2
Bagaimana Anda menggunakan kelas abstrak ini untuk menjalankan runnable dengan parameter?
EZDsIt
Saya lebih suka menggunakan konstruktor dengan parameter (s) public RunnableArg(Object... args) { setArgs(args); } kemudian menggambarkan kelas lokal: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } dan menggunakannya Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz
10

Anda memiliki dua opsi:

  1. Tentukan kelas bernama. Lewati parameter Anda ke konstruktor dari kelas bernama.

  2. Biarkan kelas anonim Anda menutup "parameter" Anda. Pastikan untuk menandainya sebagai final.

ribby
sumber
@Massa: lihat stackoverflow.com/questions/266806/…
setzamora
6

Sejak Java 8, jawaban terbaik adalah menggunakan Consumer<T>:

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

Ini adalah salah satu antarmuka fungsional, yang berarti Anda dapat menyebutnya sebagai ekspresi lambda:

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

...

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

...
Epaga
sumber
5

Pertama-tama saya ingin tahu apa yang ingin Anda capai di sini sehingga perlu argumen untuk diteruskan ke Runnable baru () atau untuk menjalankan (). Cara biasa seharusnya memiliki objek Runnable yang meneruskan data (str) ke utasnya dengan mengatur variabel anggota sebelum memulai. Metode run () kemudian menggunakan nilai-nilai variabel anggota ini untuk melakukan eksekusi someFunc ()

atlantis
sumber