พารามิเตอร์ int สุดท้ายที่ใช้ใน Runnable ที่ไม่ระบุชื่อ

ฉันสงสัยว่าฉันใช้วิธีการที่ถูกต้องเพื่อเรียกบางสิ่งในเธรดอื่นหรือไม่ ฉันกำลังทำบน Android แต่คิดว่ามันเป็นคำถาม Java ทั่วไป

ฉันมีวิธีการพร้อมพารามิเตอร์บางตัว ให้บอกว่าพวกเขาเป็น int

class Main1 {
  public static void mainOnlyWork(int x, int y) {
     // not important here.
  }
}

class Test {
  Handler mHandler;

  // called from main thread, stored as reference.
  public Test() {
    mHandler = new Handler();
  }

  public static void callInMainThread(final int x, final int y) {
    mHandler.post(new Runnable() {
      public void run() {
        Main1.mainOnlyWork(x, y);
      }
    });
  }

ตอนนี้คำถามของฉันคือ ปลอดภัยไหมที่จะใช้ int สุดท้ายเพื่อสร้างการรันแบบไม่ระบุชื่อโดยไม่มีสมาชิกคลาส หากฉันละคีย์เวิร์ดสุดท้ายสำหรับพารามิเตอร์ x และ y Eclipse จะบ่น สำหรับฉันแล้วดูเหมือนว่ามีจุดประสงค์เพื่อใช้เฉพาะตัวเลขคงที่ในกรณีเช่นนี้ ถ้าฉันผ่านไม่คงที่จะโอเคไหม? Java "ทำให้" มันคงที่โดยส่งผ่านไปยังฟังก์ชันนี้หรือไม่

แต่ฉันต้องการเรียก Test.callInMainThread จากเจ้าของภาษาโดยใช้ JNI ไม่มีทางที่ Java จะสามารถบอกได้ว่าตัวเลขเหล่านั้นเป็นค่าคงที่หรือไม่ในความคิดของฉัน ฉันสามารถไว้วางใจ Java เพื่อสร้างเวทย์มนตร์ได้หรือไม่? มันจะทำงานแบบนี้ตลอดไปหรือเปล่า?

ฉันคิดว่าบางทีฉันอาจต้องสร้างคลาสพรอกซีเช่น:

private abstract RunnableXY implements Runnable {
  public RunnableXY(int x, int y) {
    this.x = x;
    this.y = y;
  }

  public int x;
  public int y;

  public abstract void run();
}

และวิธีการโทรจะใช้:

  public static void callInMainThread(final int x, final int y) {
    mHandler.post(new RunnableXY(x,y) {
      public void run() {
        Main1.mainOnlyWork(this.x, this.y);
      }
    });
  }

ด้วยวิธีนี้ ฉันจะปกป้องค่าจากการรวบรวมขยะจนกว่า runable จะถูกใช้และทิ้งไป ฉันต้องสร้าง wrappers หรือทำเครื่องหมาย x สุดท้ายในพารามิเตอร์วิธีการว่าปลอดภัยหรือไม่ เมื่อฉันลองใช้ คำหลักสุดท้ายก็ทำงานได้ดี อย่างไรก็ตาม ในกระทู้ต่างๆ ฉันไม่คิดว่าถ้ามันใช้งานได้ตอนนี้จะเป็นเหตุผลที่มันจะใช้งานได้ตลอดไปเช่นกัน มันใช้งานได้กับขั้นสุดท้ายเสมอหรือไม่? ถ้าทำได้จะทำยังไง? จะมีความแตกต่างหรือไม่หากพารามิเตอร์ไม่ใช่ประเภทดั้งเดิม แต่เป็นวัตถุ

อัปเดต: ฉันเข้าใจแล้วว่าสุดท้ายหมายถึงอะไรใน Java นั่นไม่ใช่ประเด็นของคำถาม คำถามคือขอบเขตของตัวแปรที่ใช้ในการสร้าง Runnable อยู่ที่ไหน พวกเขาเป็นคนท้องถิ่น ซึ่งหมายความว่าหลังจากสิ้นสุดฟังก์ชันแล้ว ค่าของพวกเขาจะไม่สามารถอ้างอิงได้ ค่านั้นเก็บไว้ที่ไหนเมื่อวัตถุ Runnable ถูกส่งผ่านไปยังเธรดอื่นและรอให้ดำเนินการ


person Pihhan    schedule 23.07.2013    source แหล่งที่มา
comment
ฉันคิดว่าฉันพบคำตอบแล้ว พารามิเตอร์เหล่านั้นจะต้องเป็นพารามิเตอร์สุดท้าย เนื่องจากจะใช้เมื่อสร้างเมธอด run() ของ Runnable ที่ไม่ระบุชื่อ ถ้าฉันรัน callInMainThread(x=3, y=5) public void run() จะสร้างเนื้อหาเป็น Main1.mainOnlyWork(3, 5) โดยไม่ได้อ้างอิงถึงตัวแปรใดๆ ทั้งภายในเครื่องหรือส่วนกลาง นี่คือสิ่งที่ฉันกำลังมองหา มันถูกเก็บไว้ในโค้ด Runnable.run() ที่สร้างขึ้น ณ จุดนั้น และโค้ดจะไม่เปลี่ยนแปลงเมื่อผ่านระหว่างเธรด   -  person Pihhan    schedule 24.07.2013


คำตอบ (3)


เริ่มต้นด้วยพื้นฐาน: Java สร้าง สำเนา เมื่อคุณส่งพารามิเตอร์ x และ y ที่เมธอดของคุณได้รับไม่ใช่ตัวแปรดั้งเดิมที่ส่งผ่าน แต่เป็นสำเนาของค่าดั้งเดิม เมื่อคุณประกาศวิธีการเช่นนี้ ค่าที่คุณส่งเข้าไปไม่จำเป็นต้องเป็นค่าคงที่ แต่สำเนาที่วิธีการได้รับคือ1:

callInMainThread(final int x, final int y) { ..... }

ประการที่สอง: ไม่ คุณไม่จำเป็นต้องทำกระดาษห่อ เมื่อคุณเข้าถึงตัวแปรที่อยู่ในขอบเขตภายนอก คอมไพลเลอร์ Java จะสร้างฟิลด์เพื่อจัดเก็บโดยอัตโนมัติ เช่นเดียวกับ wrapper ที่คุณสร้างด้วยตนเอง สิ่งนี้โปร่งใสสำหรับคุณ

เหตุผลหนึ่งที่คุณไม่สามารถละเว้น final ได้ก็เนื่องมาจาก Java ไม่ได้ใช้กลไกใดๆ ในการถ่ายโอนการเปลี่ยนแปลงค่าของตัวแปรระหว่างตัวแปรโลคัลของเมธอดและฟิลด์ที่สร้างขึ้นในคลาสที่ไม่ระบุชื่อ นอกจากนี้ คลาสที่ไม่ระบุชื่ออาจมีอายุยืนยาวกว่าการเรียกเมธอด จะเกิดอะไรขึ้นหากคลาสที่ไม่ระบุชื่ออ่านหรือเขียนตัวแปรหลังจากที่เมธอดส่งคืนแล้ว ถ้าตัวแปรไม่ใช่ค่าสุดท้าย คุณจะไม่สามารถอ่านค่าของมันหรือเขียนลงไปได้อีกต่อไป


1 จริงๆ แล้ว final ตัวแปรไม่ใช่ค่าคงที่ คุณสามารถกำหนดค่าต่างๆ ให้กับค่าเหล่านั้นได้เพียงครั้งเดียวเท่านั้น ค่าของพารามิเตอร์เมธอดสุดท้ายถูกกำหนดเมื่อมีการเรียกใช้เมธอด ดังนั้นค่าของพารามิเตอร์เมธอดสุดท้ายจึงค่อนข้างคงที่ตลอดระยะเวลาของเมธอด

person Joni    schedule 23.07.2013
comment
อ่า ขอบคุณ! ฉันไม่เข้าใจว่าย่อหน้าที่สามหมายถึงอะไรกันแน่ จนกระทั่งฉันเข้าใจตัวเองเป็นคำอื่น - person Pihhan; 24.07.2013

ภายในจะมีการคัดลอกพารามิเตอร์และตัวแปรท้องถิ่น (ซึ่งอยู่ในสแต็กการเรียกเมธอด) ในเธรด เนื่องจากหลังจากการเรียกเมธอดเสร็จสิ้น เธรดยังคงอยู่

และตัวแปรจะต้องเป็นตัวแปรสุดท้าย เพื่อห้ามการเขียนทับตัวแปรดั้งเดิมในเมธอด ซึ่งจะทำให้มีเวอร์ชันอื่นในเธรด ซึ่งเป็นเพียงการทำให้เข้าใจผิด ดังนั้นจึงเป็นเรื่องของการปล่อยให้ชื่อเดียวกันทั้งสองเวอร์ชันมีความหมายเหมือนกัน

การออกแบบภาษาที่ระมัดระวัง

(ทำให้ง่ายขึ้น ไม่มีการตั้งชื่อกรณีที่กลับกันแบบสมมาตร ซึ่งยังคงเหมือนเดิม)

person Joop Eggen    schedule 23.07.2013

ประเภทดั้งเดิมจะถูกส่งผ่านตามค่าเสมอ แม้ว่าคุณจะแก้ไขภายในวิธีการ แต่ค่าดั้งเดิม (นอกวิธีการ) จะไม่เปลี่ยนแปลง ใช่ มันปลอดภัยเพราะค่าเหล่านี้อยู่ในเมธอดนั้น (อาจเป็น final หรือไม่ก็ได้)

เกี่ยวกับคำหลัก final รายการในพารามิเตอร์ จริงๆ แล้วเป็นเพียงอุปสรรคต่อคุณจากการกำหนดค่าใหม่เท่านั้น ไม่มีวัตถุประสงค์อื่นใด นี่เป็นเพียงความปลอดภัยของรหัสเท่านั้น พารามิเตอร์จะถูกส่ง เสมอ ด้วยค่าใน Java (การอ้างอิงอ็อบเจ็กต์จะถูกส่งผ่านด้วยค่าด้วย) ดังนั้นไม่ว่าจะอย่างไร การกำหนดใหม่ใดๆ ก็ตามจะอยู่ภายในเมธอดนั้น เมื่อวิธีการเสร็จสิ้น การกำหนดใหม่ทั้งหมดที่คุณทำในวิธีการนั้นจะหายไป ตัวอย่างเช่นไม่มีความแตกต่างระหว่าง

public void test(String a) {
    a = "Hello";
}

และ

public void test(final String a) {
    a = "Hello";
}

ยกเว้นว่าคอมไพเลอร์จะทำให้เกิดข้อผิดพลาดในกรณีที่สอง ในกรณีแรกเมื่อเมธอด test() เสร็จสิ้น a จะถูกคืนค่าเป็นค่าดั้งเดิม (จะไม่เป็น "Hello") สุดท้ายอย่างมีประสิทธิภาพในพารามิเตอร์ทำให้พารามิเตอร์ "คงที่" (ระวังว่าสำหรับวัตถุ: คุณยังคงสามารถแก้ไขสถานะของวัตถุได้ แต่ไม่ใช่การอ้างอิงของวัตถุ)


ต้องใช้คีย์เวิร์ด final ในคลาสที่ไม่ระบุตัวตน เนื่องจากคุณกำลังอ้างอิงตัวแปรในขอบเขตคลาสอื่น (ในกรณีของคุณ อินสแตนซ์ Runnable ที่ไม่ระบุตัวตนของคุณกำลังอ้างอิงตัวแปรอินสแตนซ์ Main1) ดังนั้นหากสิ่งนี้ทำงานในเธรดอื่นและยังไม่สิ้นสุด คุณสามารถเขียนทับการอ้างอิงดั้งเดิมตลอดระยะเวลาของเมธอดได้ ซึ่งจะทำให้แต่ละเธรดอ้างอิงอ็อบเจ็กต์ที่แตกต่างกันด้วยชื่อตัวแปรเดียวกัน ซึ่งทำให้สับสน และดังนั้นจึงถูกบล็อกในการออกแบบภาษา


คุณไม่จำเป็นต้องสร้าง Wrapper หรือข้อมูลอ้างอิงเพิ่มเติม พารามิเตอร์มีการอ้างอิงแล้วในช่วงระยะเวลาของวิธีการ และจะไม่ถูกรวบรวมแบบขยะ

person m0skit0    schedule 23.07.2013
comment
จริงๆ แล้วพวกเขาเป็นคนท้องถิ่นที่ทำงานทำให้ฉันกังวล ถ้าฉันเรียกมันสามครั้งด้วยพารามิเตอร์ที่แตกต่างกัน ให้เร็วในแถว ก่อนที่เธรดหลักจะเริ่มดำเนินการฟังก์ชันแรก พารามิเตอร์ของวิธีการถูกเปลี่ยนแปลง 3 ครั้ง ค่านิยมท้องถิ่นนั้นถูกเก็บไว้ที่ไหน? หากอยู่ในสแต็กเธรดในเครื่อง ควรเป็นไปได้ที่จะถูกเขียนทับ เธรดหลักจะต้องไม่เข้าถึงสแต็กเธรดของผู้ปฏิบัติงานใช่ไหม ints เหล่านั้นอยู่ในหน่วยความจำฮีปที่แชร์ระหว่างเธรดหรือไม่ - person Pihhan; 24.07.2013
comment
ตัวแปรในเครื่องจะถูกจัดเก็บไว้ในสแต็กเนื่องจากเป็นที่เก็บข้อมูลชั่วคราว แต่ละเธรดมีสแต็กของตัวเอง ไม่มีการแชร์ นี่คือบริบทท้องถิ่นของเธรด ไม่สามารถเขียนทับในเธรดสแต็กในเครื่องได้ ยกเว้นตามบริบทของเธรดปัจจุบัน ไม่ว่าจะเรียกเร็วแค่ไหนหรือจากที่ไหน - อันที่จริงฉันไม่สามารถเข้าใจความหมายที่แท้จริงของสิ่งนี้ได้ นอกจากนี้ ค่านี้ คัดลอก อีกครั้ง ดังนั้นไม่ว่าจะเปลี่ยนแปลงในขอบเขตท้องถิ่นหรือไม่ คุณจะเปลี่ยนสำเนาในเครื่องนั้นโดยเฉพาะ ไม่ใช่สำเนาอื่นและไม่ใช่ค่าดั้งเดิม - person m0skit0; 24.07.2013
comment
คำถามของฉันคือพวกมันถูกคัดลอกไปยัง new Runnable() {} ได้อย่างไร พวกมันถูกสร้างเป็นช่องของวัตถุนิรนามนั้นหรือไม่? ฉันต้องการทราบว่าตัวแปรเหล่านั้นถูกส่งไปยังเธรดอื่นอย่างไร ฉันเข้าใจว่าตัวแปรท้องถิ่นหมายถึงอะไร แต่ตัวแปรเหล่านั้นจะไม่ถูกใช้เมื่อมีการเรียกใช้เมธอดนั้น พวกมันจะถูกส่งต่อไปยังเมธอดซึ่งจะถูกเรียกว่าในอนาคต พวกเขาอยู่ที่ไหนในช่วงเวลาระหว่าง? - person Pihhan; 24.07.2013
comment
Runnable จะถูกสร้างขึ้นเมื่อคุณเรียกใช้เมธอดนั้น ดังนั้นค่าที่ Runnable ใช้จะเป็นค่าที่ส่งไปยังเมธอดเนื่องจากคลาสที่ไม่ระบุชื่ออยู่ภายในขอบเขตนั้น วิธีการคัดลอกไปยังอินเทอร์เฟซที่ไม่ระบุชื่อจริง ๆ แล้วขึ้นอยู่กับการใช้งานคลาส/อินเทอร์เฟซที่ไม่ระบุตัวตนของ Java IMHO สิ่งนี้ไม่เกี่ยวข้อง: คัดลอกเป็นฟิลด์หรืออะไรก็ตาม คุณสามารถตรวจสอบซอร์สโค้ด JRE ได้หากคุณต้องการทราบรายละเอียดจริงๆ - person m0skit0; 24.07.2013
comment
มันคือสิ่งที่ฉันขอ Java/Dalvik ทำให้แน่ใจว่าตัวแปรไม่เสียหายอยู่เสมอ และทำอย่างไร อาจไม่เกี่ยวข้องเลยหากฉันแน่ใจว่า VM ทำให้แน่ใจว่าสิ่งเหล่านั้นจะไม่ถูกเขียนทับหรือไม่ถูกต้อง ฉันไม่มีทักษะเพียงพอที่จะเข้าใจแหล่งที่มาของ JRE และเข้าใจว่าปลอดภัยหรือไม่ ฉันหวังว่าผู้ฉลาดกว่าจะรู้ - person Pihhan; 24.07.2013
comment
ฉันยังไม่เข้าใจว่าคุณหมายถึงอะไรโดย ตรวจสอบให้แน่ใจว่าตัวแปรไม่เสียหายเสมอ ไม่สามารถเปลี่ยนแปลงตัวแปร final ได้ โดยจะมีการตรวจสอบ ณ เวลาคอมไพล์ อีกครั้ง: พารามิเตอร์เป็นการคัดลอก บริบทเฉพาะที่สามารถแก้ไขได้ตามที่คุณต้องการ โดยจะไม่ส่งผลกระทบต่อตัวแปรเดียวกันของเธรดอื่น มันทำอย่างไร อ่านซอร์สโค้ด ถ้าคุณไม่มีความรู้ในการทำความเข้าใจซอร์ส JRE คุณก็จะไม่เข้าใจว่ามันทำอย่างไร อย่างไรก็ตาม อย่างไร จริงๆ แล้วค่อนข้างไม่เกี่ยวข้องเลย นอกจากนี้อาจมีการดำเนินการในรูปแบบที่แตกต่างกันขึ้นอยู่กับนักพัฒนา JRE คุณสามารถสรุปได้ว่า JRE กำลังทำถูกต้อง - person m0skit0; 24.07.2013