เรียกใช้กับพารามิเตอร์ได้หรือไม่


178

ฉันต้องการ "Runnable ที่ยอมรับพารามิเตอร์" แม้ว่าฉันรู้ว่า runnable ดังกล่าวไม่มีอยู่จริง

สิ่งนี้อาจชี้ไปที่ข้อบกพร่องพื้นฐานในการออกแบบแอพของฉันและ / หรือบล็อกจิตในสมองที่เหนื่อยล้าของฉันดังนั้นฉันหวังว่าจะได้รับคำแนะนำเกี่ยวกับวิธีการทำสิ่งต่อไปนี้โดยไม่ละเมิดหลักการพื้นฐานของ OO:

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

ความคิดวิธีการทำสิ่งที่ต้องการข้างต้น?


7
Consumer<T>ตอนนี้คุณสามารถใช้
Alex78191

1
ฉันอ่านคำตอบของคำถามนี้แล้ว ดูเหมือนว่าฉันแปลกที่ไม่มีใครบอกว่าคุณสามารถเพิ่ม Runnables ที่คุณต้องการในโครงการของคุณ (ด้วยหนึ่ง, สอง, สามหรือมากกว่า args) เพียงเพิ่มอินเทอร์เฟซที่เหมาะสม ฉันสร้างส่วนแสดงความคิดเห็นที่นี่สำหรับผู้ที่สนใจ: gist.github.com/jsfan3/3a66e711fd0fd233c5e4c467184adb7a
Francesco Galgani

นี่ไม่ใช่การซ้ำกับ "ฉันจะส่งพารามิเตอร์ไปยังเธรด Java ได้อย่างไร" และคำตอบที่ทันสมัยคือเช่น @ Alex78191 พูดว่า: useConsumer<T>
Epaga

คำตอบ:


228

เป็นเวลาเกือบ 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();
}

ขอบคุณทุกคน! โซลูชันที่แนะนำทั้งหมดชี้ไปที่แนวทางเดียวกัน แต่ฉันยอมรับได้เพียงวิธีเดียวเท่านั้น ฉันต้องเหนื่อยมากที่ไม่สามารถทำสิ่งนี้ด้วยตัวเองได้ +1 ให้กับทุกคน
uTubeFan

18
จริงๆแล้วคนส่วนใหญ่ไม่รู้ว่าคุณสามารถประกาศชั้นเรียนได้ บางคนคิดว่ามันเป็นสไตล์ที่ไม่ดี ฉันคิดว่ามันเป็นเรื่องของรสนิยม :)
corsiKa

3
ส่วนต่อประสานสาธารณะ ResultRunnable <T> {โมฆะสาธารณะรัน (ผล T); }
Roman M

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;

}

(เมื่อฉันใช้สิ่งนี้พารามิเตอร์ของฉันคือเลขจำนวนเต็มซึ่งฉันใช้ในการทำ hashmap ของ ID -> myRunnables ด้วยวิธีนี้ฉันสามารถใช้ hashmap เพื่อโพสต์ / ลบวัตถุ myRunnable ที่แตกต่างกันในตัวจัดการ)


3
ขอขอบคุณที่แบ่งปันรหัส - ฉันรักเมื่อมีคนทำอย่างนั้นแทนที่จะเป็นคนพูดไร้สาระ คำถามหนึ่ง - อยู่เหนือวิธีการตกลงเมื่อมาถึงหน่วยความจำรั่ว ข้อมูลอ้างอิงทั้งหมดที่คุณผ่านจะถูกกำจัดอย่างเหมาะสมหรือไม่
nikib3ro

2
@ kape123 คำตอบคือ "มันขึ้นอยู่กับ" ตราบใดที่Runnableวัตถุที่ส่งคืนโดยวิธีการนั้นมีอยู่ทุกที่ค่าparamStrคงอาจไม่เหมาะสมสำหรับการรวบรวมขยะ เป็นไปได้ว่าหากวัตถุนั้นมีอยู่ แต่ไม่สามารถเรียกใช้อีกครั้งได้ JIT (หรือแม้กระทั่ง javac) อาจตัดสินใจที่จะลบมันออกจากขอบเขต แต่เราไม่ควรพึ่งพาการปรับให้เหมาะสมดังกล่าวเพราะอาจมีการเปลี่ยนแปลงในอนาคต
corsiKa

"ขอบคุณที่แบ่งปันรหัส - ฉันชอบเวลาที่คนอื่นทำแทนที่จะเป็นคนพูดไร้สาระ" - อะไร?
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));

สร้างฟังก์ชั่น 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
คุณจะใช้คลาสนามธรรมนี้เพื่อรัน runnable ด้วยพารามิเตอร์ได้อย่างไร
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 ()

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.