กลับมาจากการบล็อกในที่สุดใน Java


177

ฉันรู้สึกประหลาดใจเมื่อเร็ว ๆ นี้ที่พบว่ามีความเป็นไปได้ที่จะมีข้อความสั่งคืนในบล็อกสุดท้ายใน Java

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

ดังนั้นคำถามของฉันคือทุกคนสามารถให้ฉันตัวอย่างที่คำสั่งคืน (หรือการควบคุมการไหลอื่น ๆ ) ในที่สุดบล็อกสร้างรหัสที่อ่านได้ดีขึ้น / มากขึ้น?

คำตอบ:


90

ตัวอย่างที่คุณให้มามีเหตุผลเพียงพอที่จะไม่ใช้การควบคุมการไหลในที่สุด

แม้ว่าจะมีตัวอย่างที่วางแผนไว้ซึ่ง "ดีกว่า" ให้พิจารณาผู้พัฒนาที่ต้องรักษารหัสของคุณในภายหลังและผู้ที่อาจไม่ทราบรายละเอียดปลีกย่อย นักพัฒนาที่น่าสงสารคนนั้นอาจเป็นคุณ ....


5
แน่ใจ ฉันเดาว่าฉันจะถามในกรณีที่มีคนให้ตัวอย่างที่น่าสนใจแก่ฉันในด้านของความดี
Matt Sheppard

@MattSheppard ใน DAOS ผมมักจะเข้าสู่ระบบออกจากแบบสอบถามในลองสุดท้าย
เบลค

148

ฉันมีเวลายากมากที่จะติดตามข้อผิดพลาดเมื่อหลายปีก่อนที่เกิดจากสิ่งนี้ รหัสคือสิ่งที่ชอบ:

Object problemMethod() {
    Object rtn = null;
    try {
        rtn = somethingThatThrewAnException();
    }
    finally {
        doSomeCleanup();
        return rtn;
    }
}

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

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


ในกรณีที่ควรกลับมา?
parsecer

@parsecer ฉันจะบอกว่าหลังจากโทรหาบางอย่าง ThatThrewAnException () ภายในบล็อกลองใช้
Tiago Sippert

@parsecer, ?? เพียงทำตามวิธีปกติหลังจากในที่สุด
Pacerier

21

javac จะเตือนการกลับมาในที่สุดหากคุณใช้ -Xlint: ในที่สุด เริ่มแรก javac ไม่มีคำเตือน - หากมีบางอย่างผิดปกติกับรหัสมันควรจะล้มเหลวในการรวบรวม น่าเสียดายที่ความเข้ากันได้ย้อนหลังหมายถึงความโง่เขลาที่ไม่คาดคิดที่ไม่คาดคิดไม่สามารถห้ามได้

ข้อยกเว้นสามารถถูกโยนออกจากบล็อกในที่สุด แต่ในกรณีนั้นพฤติกรรมที่จัดแสดงเป็นสิ่งที่คุณต้องการ


13

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

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

สิ่งใดที่มีผลต่อการกลับมาของฟังก์ชันควรอยู่ในบล็อก try {} แม้ว่าคุณจะมีวิธีการที่คุณตรวจสอบสถานะภายนอกใช้เวลานานในการดำเนินการจากนั้นตรวจสอบสถานะนั้นอีกครั้งในกรณีที่มันไม่ถูกต้องคุณยังคงต้องการการตรวจสอบครั้งที่สองในการลอง {} - หากนั่งภายใน และการดำเนินการที่ยาวนานล้มเหลวจากนั้นคุณจะตรวจสอบสถานะนั้นเป็นครั้งที่สองโดยไม่จำเป็น


6

การทดสอบ Groovy อย่างง่าย:

public class Instance {

  List<String> runningThreads = new ArrayList<String>()

  void test(boolean returnInFinally) {

    println "\ntest(returnInFinally: $returnInFinally)"
    println "--------------------------------------------------------------------------"
    println "before execute"
    String result = execute(returnInFinally, false)
    println "after execute -> result: " + result
    println "--------------------------------------------------------------------------"

    println "before execute"
    try {
      result = execute(returnInFinally, true)
      println "after execute -> result: " + result
    } catch (Exception ex) {
      println "execute threw exception: " + ex.getMessage()
    }  
    println "--------------------------------------------------------------------------\n"

  }

  String execute(boolean returnInFinally, boolean throwError) {
      String thread = Thread.currentThread().getName()
      println "...execute(returnInFinally: $returnInFinally, throwError: $throwError) - thread: $thread"
      runningThreads.add(thread)
      try {
        if (throwError) {
          println "...error in execute, throw exception"
          throw new Exception("as you liked :-)")
        }
        println "...return 'OK' from execute"
        return "OK"
      } finally {
        println "...pass finally block"
        if (returnInFinally) return "return value from FINALLY ^^"
        // runningThreads.remove(thread)
      }
  }
}

Instance instance = new Instance()
instance.test(false)
instance.test(true)

เอาท์พุท:

test(returnInFinally: false)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: OK
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
execute threw exception: as you liked :-)
-----------------------------------------------------------------------------


test(returnInFinally: true)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------

คำถาม:

จุดหนึ่งที่น่าสนใจสำหรับฉันคือการดูว่า Groovy จัดการกับผลตอบแทนโดยนัยอย่างไร ใน Groovy มีความเป็นไปได้ที่จะ "คืนค่า" จากเมธอดเพียงแค่ปล่อยค่าไว้ที่ส่วนท้าย (โดยไม่ส่งคืน) คุณคิดอย่างไรกับสิ่งที่เกิดขึ้นหากคุณไม่แสดงความคิดเห็นในบรรทัดrunningThreads.remove (.. )ในคำสั่งสุดท้าย - สิ่งนี้จะเขียนทับค่าส่งคืนปกติ ("ตกลง") และครอบคลุมข้อยกเว้นหรือไม่!


0

การกลับมาจากภายในfinallyบล็อกจะทำให้เกิดexceptionsหลงทาง

คำสั่งการส่งคืนภายในบล็อกในที่สุดจะทำให้เกิดข้อยกเว้นใด ๆ ที่อาจถูกโยนในการลองหรือบล็อกการจับที่จะทิ้ง

ตามข้อกำหนดภาษา Java:

หากการดำเนินการของบล็อกทดลองเสร็จสมบูรณ์อย่างกะทันหันด้วยเหตุผลอื่น R แล้วบล็อกสุดท้ายจะถูกดำเนินการและจากนั้นก็มีทางเลือก:

   If the finally block completes normally, then the try statement
   completes  abruptly for reason R.

   If the finally block completes abruptly for reason S, then the try
   statement  completes abruptly for reason S (and reason R is
   discarded).

หมายเหตุ: ตามJLS 14.17 - ข้อความสั่งคืนจะเสร็จสมบูรณ์ในทันที

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