Runnable với một tham số?


178

Tôi có nhu cầu "Runnable chấp nhận tham số" mặc dù tôi biết rằng runnable đó không thực sự tồn tại.

Điều này có thể chỉ ra lỗ hổng cơ bản trong thiết kế ứng dụng của tôi và / hoặc khối tâm thần trong bộ não mệt mỏi của tôi, vì vậy tôi hy vọng tìm thấy ở đây một số lời khuyên về cách thực hiện một cái gì đó như sau, mà không vi phạm các nguyên tắc OO cơ bản:

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

Bất kỳ ý tưởng làm thế nào để thực hiện một cái gì đó như ở trên?


7
Bây giờ bạn có thể sử dụng Consumer<T>.
Alex78191

1
Tôi đã đọc các câu trả lời khác nhau cho câu hỏi này. Có vẻ lạ là tôi không ai nói rằng bạn có thể thêm vào dự án của mình Runnables mà bạn cần (với một, hai, ba hoặc nhiều đối số) chỉ cần thêm một giao diện thích hợp. Tôi đã tạo một ý chính nhận xét ở đây cho những ai quan tâm: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Francesco Galgani

Đây KHÔNG phải là một bản sao để "Làm thế nào tôi có thể truyền tham số cho một luồng Java". Và câu trả lời hiện đại là, như @ Alex78191 nói: sử dụngConsumer<T>
Epaga

Câu trả lời:


228

Chà, đã gần 9 năm kể từ khi tôi đăng bài này và thành thật mà nói, Java đã có một vài cải tiến kể từ đó. Tôi sẽ để lại câu trả lời ban đầu của mình bên dưới, nhưng không cần mọi người làm những gì trong đó. 9 năm trước, trong quá trình xem xét mã tôi đã đặt câu hỏi tại sao họ làm điều đó và có thể phê duyệt nó, có thể không. Với lambdas hiện đại có sẵn, thật vô trách nhiệm khi có một câu trả lời được đánh giá cao như vậy đề xuất một cách tiếp cận cổ xưa (rằng, về mặt công bằng, đã bị nghi ngờ bắt đầu bằng ...) Trong Java hiện đại, đánh giá mã đó sẽ bị từ chối ngay lập tức, và điều này sẽ bị từ chối ngay lập tức đề nghị:

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

Như trước đây, các chi tiết như xử lý chủ đề đó một cách có ý nghĩa được để lại như một bài tập cho người đọc. Nhưng nói thẳng ra, nếu bạn sợ sử dụng lambdas, bạn thậm chí còn sợ hơn các hệ thống đa luồng.

Câu trả lời gốc, chỉ vì:

Bạn có thể khai báo một lớp ngay trong phương thức

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

Cảm ơn tất cả! Tất cả các giải pháp được đề xuất đều chỉ ra cùng một cách tiếp cận nhưng tôi chỉ có thể chấp nhận một. Tôi phải rất mệt mỏi khi không thể tự mình nghĩ ra điều này. +1 cho tất cả.
uTubeFan

18
Trên thực tế, hầu hết mọi người không biết bạn có thể khai báo một lớp bên trong một phương thức. Một số người sẽ coi đó là phong cách nghèo nàn. Tôi đoán đó là vấn đề của hương vị. :)
corsiKa

3
giao diện công cộng resultRunnable <T> {public void run (kết quả T); }
Roman M

46

Bạn có thể đặt nó trong một chức năng.

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;

}

(Khi tôi sử dụng thông số này, tham số của tôi là ID số nguyên, mà tôi đã sử dụng để tạo một hashmap của ID -> myRunnables. Bằng cách đó, tôi có thể sử dụng hàm băm để đăng / xóa các đối tượng myRunnable khác nhau trong trình xử lý.)


3
Cảm ơn vì đã chia sẻ mã - Tôi thích khi mọi người làm điều đó thay vì chỉ nói lung tung. Một câu hỏi - cách tiếp cận trên có ổn không khi nói đến Rò rỉ bộ nhớ? Tất cả các tài liệu tham khảo bạn vượt qua sẽ được xử lý đúng?
nikib3ro

2
@ kape123 Câu trả lời là "nó phụ thuộc". Miễn là một Runnableđối tượng được trả về bởi phương thức tồn tại ở bất cứ đâu, paramStrcó thể sẽ không đủ điều kiện để thu gom rác. Có thể là nếu đối tượng tồn tại nhưng không bao giờ có thể chạy lại được, JIT (hoặc thậm chí javac) có thể quyết định xóa nó khỏi phạm vi, nhưng chúng ta không nên dựa vào tối ưu hóa như vậy vì chúng có thể thay đổi trong tương lai.
corsiKa

"Cảm ơn vì đã chia sẻ mã - tôi thích khi mọi người làm điều đó thay vì chỉ nói lung tung." - gì?
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));

Tạo hàm init trả về chính đối tượng và khởi tạo tham số với nó.


1
Thật không may, bạn sẽ không thể gán nó cho một biến và gọi init ()
Andrew Glukhoff

11

Tôi sử dụng lớp sau đây thực hiện giao diện Runnable . Với lớp này, bạn có thể dễ dàng tạo chủ đề mới với các đối số

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
Làm thế nào bạn sẽ sử dụng lớp trừu tượng này để chạy một runnable với một tham số?
EZDsIt

Tôi thích sử dụng hàm tạo với tham số (s) public RunnableArg(Object... args) { setArgs(args); } sau đó mô tả lớp cục bộ: class ActionArg extends RunnableArg { public ActionArg(Object... arg) { super(arg); } @Override public void run() { /* getArgs() and process them */ } } và sử dụng nó Thread t = new Thread(new ActionArg( %param_object(s)% )); t.start();
GSD.Aaz

10

Bạn có hai lựa chọn:

  1. Xác định một lớp được đặt tên. Truyền tham số của bạn cho hàm tạo của lớp được đặt tên.

  2. Có lớp ẩn danh của bạn gần với "tham số" của bạn. Hãy chắc chắn để đánh dấu nó là final.



6

Kể từ Java 8, câu trả lời tốt nhất là sử dụng Consumer<T>:

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

Đây là một trong những giao diện chức năng, có nghĩa là bạn có thể gọi nó là biểu thức lambda:

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

...

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

...

5

Trước tiên tôi muốn biết những gì bạn đang cố gắng thực hiện ở đây để cần một đối số được chuyển sang Runnable () mới hoặc để chạy (). Cách thông thường là có một đối tượng Runnable chuyển dữ liệu (str) đến các luồng của nó bằng cách đặt các biến thành viên trước khi bắt đầu. Phương thức run () sau đó sử dụng các giá trị biến thành viên này để thực hiện someFunc ()

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.