สำนวนที่ถูกต้องสำหรับการจัดการทรัพยากรที่ถูกผูกมัดในลองกับทรัพยากร


168

ไวยากรณ์ของJava 7 try-with-resources (หรือที่รู้จักในชื่อ ARM block ( การจัดการทรัพยากรอัตโนมัติ )) เป็นสิ่งที่ดีสั้นและตรงไปตรงมาเมื่อใช้AutoCloseableทรัพยากรเพียงแหล่งเดียว อย่างไรก็ตามฉันไม่แน่ใจว่าสำนวนที่ถูกต้องคืออะไรเมื่อฉันต้องการประกาศหลาย ๆ ทรัพยากรที่ต้องพึ่งพาซึ่งกันและกันตัวอย่างเช่น a FileWriterและ a BufferedWriterที่ล้อมรอบ แน่นอนว่าคำถามนี้เกี่ยวข้องกับกรณีใด ๆ เมื่อAutoCloseableมีการห่อทรัพยากรไม่เฉพาะชั้นเรียนเฉพาะสองแห่งนี้

ฉันมากับทางเลือกทั้งสามต่อไปนี้:

1)

สำนวนไร้เดียงสาที่ฉันเห็นคือประกาศเฉพาะเสื้อคลุมระดับบนสุดในตัวแปรที่จัดการโดย ARM:

static void printToFile1(String text, File file) {
    try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }
}

อันนี้ดีและสั้น แต่มันก็หัก เนื่องจากพื้นฐานFileWriterไม่ได้ถูกประกาศในตัวแปรมันจะไม่ถูกปิดโดยตรงในfinallyบล็อกที่สร้างขึ้น มันจะถูกปิดโดยcloseวิธีการห่อBufferedWriterเท่านั้น ปัญหาคือว่าถ้ายกเว้นจะโยนจากbwคอนสตรัค 's มันcloseจะไม่ถูกเรียกและดังนั้นจึงพื้นฐานจะไม่ถูกปิดFileWriter

2)

static void printToFile2(String text, File file) {
    try (FileWriter fw = new FileWriter(file);
            BufferedWriter bw = new BufferedWriter(fw)) {
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }
}

ที่นี่ทั้งทรัพยากรพื้นฐานและทรัพยากรห่อถูกประกาศในตัวแปรที่จัดการโดย ARM ดังนั้นทั้งสองอย่างจะปิดอย่างแน่นอน แต่ข้อมูลพื้นฐานfw.close() จะถูกเรียกสองครั้ง : ไม่เพียงโดยตรง แต่ยังผ่านการตัดคำbw.close()ด้วย

นี่ไม่ควรเป็นปัญหาสำหรับคลาสเฉพาะสองเหล่านี้ที่ทั้งสองนำมาใช้Closeable(ซึ่งเป็นชนิดย่อยของAutoCloseable), ซึ่งสัญญาระบุว่าcloseอนุญาตให้มีการโทรหลายสายได้:

ปิดสตรีมนี้และปล่อยทรัพยากรระบบใด ๆ ที่เกี่ยวข้อง หากกระแสข้อมูลถูกปิดอยู่แล้วการเรียกใช้วิธีนี้จะไม่มีผลกระทบ

อย่างไรก็ตามในกรณีทั่วไปฉันสามารถมีทรัพยากรที่ใช้งานได้เท่านั้นAutoCloseable(และไม่Closeable) ซึ่งไม่รับประกันว่าcloseสามารถเรียกได้หลายครั้ง:

โปรดทราบว่าแตกต่างจากวิธีการปิดของ java.io.Closeable วิธีการปิดนี้ไม่จำเป็นต้องเป็น idempotent กล่าวอีกนัยหนึ่งการเรียกใช้วิธีการปิดนี้มากกว่าหนึ่งครั้งอาจมีผลข้างเคียงที่มองเห็นได้ซึ่งแตกต่างจาก Closeable.close ซึ่งจำเป็นต้องมีผลถ้าเรียกว่ามากกว่าหนึ่งครั้ง อย่างไรก็ตามอินเทอร์เฟซของอินเทอร์เฟซนี้ได้รับการสนับสนุนอย่างยิ่งเพื่อให้วิธีการปิด idempotent ของพวกเขา

3)

static void printToFile3(String text, File file) {
    try (FileWriter fw = new FileWriter(file)) {
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }
}

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

ในทางตรงกันข้ามไวยากรณ์นั้นค่อนข้างผิดปกติและ Eclipse ก็ออกคำเตือนซึ่งฉันเชื่อว่าเป็นสัญญาณเตือนที่ผิด แต่ก็ยังคงเป็นคำเตือนว่ามีการจัดการกับ:

การรั่วไหลของทรัพยากร: 'bw' ไม่เคยถูกปิด


ดังนั้นวิธีการที่จะไปเพื่อ? หรือว่าฉันคิดถึงสำนวนอื่นที่ถูกต้อง ?


4
แน่นอนว่าถ้าตัวสร้างของ FileWriter พื้นฐานโยนข้อยกเว้นมันจะไม่เปิดขึ้นและทุกอย่างก็โอเค ตัวอย่างที่ 1 คืออะไรจะเกิดอะไรขึ้นถ้า FileWriter ถูกสร้างขึ้น แต่ Constructor ของ BufferedWriter จะแสดงข้อยกเว้น
Natix

6
มันเป็นที่น่าสังเกตว่า BufferedWriter จะไม่ส่งข้อยกเว้น มีตัวอย่างที่คุณสามารถคิดได้หรือไม่ว่าคำถามนี้ไม่ได้เป็นวิชาการที่บริสุทธิ์
Peter Lawrey

10
@PeterLawrey ใช่คุณคิดถูกแล้วว่า Constructor ของ BufferedWriter ในสถานการณ์นี้ส่วนใหญ่จะไม่ยกเว้น แต่อย่างที่ฉันได้ชี้ให้เห็น แต่สำหรับตัวอย่างเช่นสามารถโยนpublic BufferedWriter(Writer out, int sz) IllegalArgumentExceptionนอกจากนี้ฉันสามารถขยาย BufferedWriter ด้วยคลาสที่จะโยนบางสิ่งบางอย่างจากตัวสร้างหรือสร้าง wrapper แบบกำหนดเองใด ๆ ที่ฉันต้องการ
Natix

5
ตัวBufferedWriterสร้างสามารถโยนข้อยกเว้นได้อย่างง่ายดาย OutOfMemoryErrorอาจเป็นสิ่งที่พบได้บ่อยที่สุดในขณะที่จัดสรรหน่วยความจำที่พอเหมาะสำหรับบัฟเฟอร์ (แม้ว่าอาจระบุว่าคุณต้องการรีสตาร์ทกระบวนการทั้งหมด) / คุณจำเป็นต้องflushของคุณBufferedWriterถ้าคุณทำไม่ได้ใกล้ชิดและต้องการให้เนื้อหา (โดยทั่วไปเฉพาะกรณีที่ไม่ใช่ข้อยกเว้น) FileWriterรับสิ่งที่เกิดขึ้นเป็นการเข้ารหัสไฟล์ "เริ่มต้น" ดีกว่าที่จะชัดเจน
Tom Hawtin - tackline

10
@Natix ฉันหวังว่าคำถามทั้งหมดใน SO นั้นได้รับการวิจัยและชัดเจนเช่นนี้ ฉันหวังว่าฉันจะลงคะแนนได้ 100 ครั้ง
Geek

คำตอบ:


75

นี่คือทางเลือกของฉัน:

1)

try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
    bw.write(text);
}

สำหรับฉันสิ่งที่ดีที่สุดที่มากับ Java จาก C ++ แบบดั้งเดิมเมื่อ 15 ปีก่อนคือคุณสามารถไว้วางใจโปรแกรมของคุณได้ แม้ว่าสิ่งที่อยู่ในโคลนและผิดไปซึ่งพวกเขามักจะทำฉันต้องการให้รหัสที่เหลืออยู่ในพฤติกรรมที่ดีที่สุดและกลิ่นของดอกกุหลาบ อันที่จริงBufferedWriterอาจมีข้อยกเว้นที่นี่ หน่วยความจำไม่เพียงพอจะไม่ผิดปกติตัวอย่างเช่น สำหรับนักตกแต่งอื่น ๆ คุณรู้หรือไม่ว่าjava.ioคลาส wrapper ใดที่มีข้อยกเว้นที่ตรวจสอบจาก Constructor ของพวกเขา ฉันไม่. การทำความเข้าใจโค้ดไม่ได้ดีนักถ้าคุณพึ่งพาความรู้ที่คลุมเครือนั้น

นอกจากนี้ยังมี "การทำลาย" หากมีเงื่อนไขข้อผิดพลาดคุณอาจไม่ต้องการล้างข้อมูลลงไฟล์ที่ต้องการลบ (รหัสที่ไม่แสดง) แม้ว่าแน่นอนว่าการลบไฟล์นั้นเป็นอีกหนึ่งการดำเนินการที่น่าสนใจในการจัดการข้อผิดพลาด

โดยทั่วไปคุณต้องการfinallyบล็อกให้สั้นและเชื่อถือได้มากที่สุด การเพิ่มฟลัชไม่ช่วยให้เป้าหมายนี้ สำหรับหลาย ๆ รุ่นของคลาสบัฟเฟอร์บางตัวใน JDK มีข้อผิดพลาดที่เกิดจากข้อยกเว้นflushภายในที่closeเกิดจากcloseการตกแต่งวัตถุที่ไม่ได้ถูกเรียก ในขณะที่ได้รับการแก้ไขบางครั้งคาดว่าจะมาจากการใช้งานอื่น ๆ

2)

try (
    FileWriter fw = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(fw)
) {
    bw.write(text);
}

เรายังคงล้างในบล็อกนัยที่สุด (ตอนนี้ซ้ำแล้วซ้ำอีกclose- นี้ได้รับเลวร้ายยิ่งเป็นคุณเพิ่มตกแต่งเพิ่มเติม) แต่การก่อสร้างที่มีความปลอดภัยและเราจะต้องนัยที่สุดบล็อกดังนั้นแม้ล้มเหลวflushปล่อยทรัพยากรที่ไม่ได้ป้องกัน

3)

try (FileWriter fw = new FileWriter(file)) {
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(text);
}

มีข้อบกพร่องอยู่ที่นี่ ควรจะเป็น:

try (FileWriter fw = new FileWriter(file)) {
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(text);
    bw.flush();
}

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

คำตัดสิน

แม้ว่า 3 เป็นโซลูชั่นที่เหนือกว่าในด้านเทคนิค แต่เหตุผลในการพัฒนาซอฟต์แวร์ทำให้ 2 เป็นตัวเลือกที่ดีกว่า อย่างไรก็ตามการลองกับทรัพยากรยังคงเป็นการแก้ไขที่ไม่เพียงพอและคุณควรใช้Execute Around idiomซึ่งควรมีไวยากรณ์ที่ชัดเจนยิ่งขึ้นด้วยการปิดใน Java SE 8


4
ในเวอร์ชัน 3 คุณจะรู้ได้อย่างไรว่า bw ไม่ต้องการการเรียกปิด และแม้ว่าคุณจะมั่นใจได้หรือไม่นั่นก็เป็น "ความรู้ที่คลุมเครือ" เช่นเดียวกับที่คุณพูดถึงรุ่น 1 ใช่หรือไม่
TimK

3
" เหตุผลในการพัฒนาซอฟต์แวร์ทำให้ 2 ตัวเลือกที่ดีกว่า " คุณสามารถอธิบายรายละเอียดเพิ่มเติมนี้ได้หรือไม่
Duncan Jones

8
คุณช่วยยกตัวอย่างของ "Execute Around idiom with closures" ได้ไหม
Markus

2
คุณสามารถอธิบายว่า "ไวยากรณ์ที่ชัดเจนยิ่งขึ้นด้วยการปิดใน Java SE 8" คืออะไร?
petertc

1
ตัวอย่างของ "Execute Around idiom" อยู่ที่นี่: stackoverflow.com/a/342016/258772
mrts

20

สไตล์แรกเป็นสไตล์ที่Oracleแนะนำ BufferedWriterไม่โยนข้อยกเว้นที่ตรวจสอบดังนั้นหากมีข้อผิดพลาดเกิดขึ้นแสดงว่าโปรแกรมไม่คาดว่าจะกู้คืนจากมันทำให้การกู้คืนทรัพยากรส่วนใหญ่เป็นข้อสงสัย

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


2
เป็นแนวทางที่แนะนำใน Effective Java รุ่นที่ 3
shmosel

5

ตัวเลือก 4

เปลี่ยนทรัพยากรของคุณเป็นแบบปิดได้ไม่ใช่ AutoClosable หากคุณทำได้ ความจริงที่ว่าคอนสตรัคสามารถถูกล่ามโซ่หมายถึงมันไม่ได้เป็นไม่เคยได้ยินของการปิดทรัพยากรสองครั้ง (นี่เป็นความจริงก่อน ARM ด้วย) ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ด้านล่าง

ตัวเลือก 5

อย่าใช้ ARM และรหัสอย่างระมัดระวังเพื่อให้แน่ใจว่าจะไม่เรียกว่า close () สองครั้ง!

ตัวเลือก 6

อย่าใช้ ARM และให้คุณปิดการโทร () ในที่สุดเพื่อลอง / รับสายเอง

ทำไมฉันไม่คิดว่าปัญหานี้มีลักษณะเฉพาะกับ ARM

ในตัวอย่างเหล่านี้ทั้งหมดการเรียกปิด () สุดท้ายควรอยู่ใน catch block เหลือไว้สำหรับการอ่าน

ไม่ดีเพราะ fw สามารถปิดได้สองครั้ง (ซึ่งดีสำหรับ FileWriter แต่ไม่อยู่ในตัวอย่างของคุณ):

FileWriter fw = null;
BufferedWriter bw = null;
try {
  fw = new FileWriter(file);
  bw = new BufferedWriter(fw);
  bw.write(text);
} finally {
  if ( fw != null ) fw.close();
  if ( bw != null ) bw.close();
}

ไม่ดีเพราะ fw ไม่ปิดถ้ามีข้อยกเว้นในการสร้าง BufferedWriter (ไม่สามารถเกิดขึ้นได้อีก แต่ในตัวอย่างสมมุติของคุณ):

FileWriter fw = null;
BufferedWriter bw = null;
try {
  fw = new FileWriter(file);
  bw = new BufferedWriter(fw);
  bw.write(text);
} finally {
  if ( bw != null ) bw.close();
}

3

ฉันแค่อยากจะสร้างตามคำแนะนำของ Jeanne Boyarsky ที่ไม่ได้ใช้ ARM แต่ให้แน่ใจว่า FileWriter จะปิดทันทีหนึ่งครั้ง อย่าคิดว่ามีปัญหาใด ๆ ที่นี่ ...

FileWriter fw = null;
BufferedWriter bw = null;
try {
    fw = new FileWriter(file);
    bw = new BufferedWriter(fw);
    bw.write(text);
} finally {
    if (bw != null) bw.close();
    else if (fw != null) fw.close();
}

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


5
หากทั้งคุณtryและfinallyบล็อกมีข้อยกเว้นการสร้างนี้จะสูญเสียหนึ่ง (และอาจมีประโยชน์มากขึ้น) หนึ่ง
rxg

3

หากต้องการเห็นพ้องกับความคิดเห็นก่อนหน้านี้: ง่ายที่สุดคือ(2)ใช้Closeableทรัพยากรและประกาศตามลำดับในข้อลองกับทรัพยากร หากคุณมีเพียงAutoCloseableคุณสามารถตัดพวกเขาในอีก (ซ้อน) ชั้นที่เพียงแค่การตรวจสอบที่closeเรียกว่าเพียงครั้งเดียว (แบบซุ้ม) private bool isClosed;เช่นโดยมี ในทางปฏิบัติแม้แต่ Oracle เพียง(1) เชื่อมโยงคอนสตรัคเตอร์และไม่จัดการข้อยกเว้นบางส่วนผ่านทางเชน

อีกวิธีหนึ่งคุณสามารถสร้างทรัพยากรที่ถูกโยงด้วยตนเองโดยใช้วิธีการแบบคงที่จากโรงงาน สิ่งนี้จะห่อหุ้มโซ่และจัดการกับการล้างข้อมูลถ้ามันล้มเหลวทาง:

static BufferedWriter createBufferedWriterFromFile(File file)
  throws IOException {
  // If constructor throws an exception, no resource acquired, so no release required.
  FileWriter fileWriter = new FileWriter(file);
  try {
    return new BufferedWriter(fileWriter);  
  } catch (IOException newBufferedWriterException) {
    try {
      fileWriter.close();
    } catch (IOException closeException) {
      // Exceptions in cleanup code are secondary to exceptions in primary code (body of try),
      // as in try-with-resources.
      newBufferedWriterException.addSuppressed(closeException);
    }
    throw newBufferedWriterException;
  }
}

จากนั้นคุณสามารถใช้มันเป็นทรัพยากรเดียวในมาตราลองกับทรัพยากร:

try (BufferedWriter writer = createBufferedWriterFromFile(file)) {
  // Work with writer.
}

ความซับซ้อนมาจากการจัดการข้อยกเว้นหลายประการ ไม่อย่างนั้นเป็นเพียง "ทรัพยากรปิดที่คุณได้รับมา" การปฏิบัติทั่วไปดูเหมือนว่าจะเริ่มต้นตัวแปรที่เก็บวัตถุที่เก็บทรัพยากรไว้null(ที่นี่fileWriter) ก่อนจากนั้นรวมการตรวจสอบค่าว่างในการล้างข้อมูล แต่ดูเหมือนว่าไม่จำเป็น: ถ้าตัวสร้างล้มเหลว ดังนั้นเราสามารถปล่อยให้ข้อยกเว้นนั้นแพร่กระจายซึ่งทำให้รหัสง่ายขึ้นเล็กน้อย

คุณอาจทำสิ่งนี้โดยทั่วไป:

static <T extends AutoCloseable, U extends AutoCloseable, V>
    T createChainedResource(V v) throws Exception {
  // If constructor throws an exception, no resource acquired, so no release required.
  U u = new U(v);
  try {
    return new T(u);  
  } catch (Exception newTException) {
    try {
      u.close();
    } catch (Exception closeException) {
      // Exceptions in cleanup code are secondary to exceptions in primary code (body of try),
      // as in try-with-resources.
      newTException.addSuppressed(closeException);
    }
    throw newTException;
  }
}

ในทำนองเดียวกันคุณสามารถเชื่อมโยงทรัพยากรสามอย่างเข้าด้วยกันเป็นต้น

คุณสามารถโยงโซ่ได้สามครั้งด้วยการโยงทรัพยากรสองครั้งและมันจะเชื่อมโยงซึ่งหมายความว่าคุณจะได้รับวัตถุเดียวกันกับความสำเร็จ (เพราะตัวสร้างมีความสัมพันธ์) และข้อยกเว้นเดียวกันหากมีความล้มเหลว ในการก่อสร้างใด ๆ สมมติว่าคุณเพิ่มSโซ่ข้างต้น (เพื่อให้คุณเริ่มต้นด้วยVและสิ้นสุดกับSโดยใช้U , TและSในทางกลับกัน) คุณจะได้รับเหมือนกันอย่างใดอย่างหนึ่งถ้าคุณห่วงโซ่แรกSและTแล้วU , สอดคล้องกับ(ST) Uหรือหากคุณถูกล่ามโซ่TและUก่อนSสอดคล้องกับS (TU) อย่างไรก็ตามมันจะมีความชัดเจนมากขึ้นเพียงแค่เขียนโซ่สามเท่าที่ชัดเจนในฟังก์ชั่นโรงงานเดียว


ฉันกำลังรวบรวมได้อย่างถูกต้องที่หนึ่งยังคงต้องใช้ลองกับทรัพยากรในขณะที่try (BufferedWriter writer = <BufferedWriter, FileWriter>createChainedResource(file)) { /* work with writer */ }?
ErikE

@ErikE ใช่คุณจะยังคงต้องใช้การลองกับทรัพยากร แต่คุณจะต้องใช้ฟังก์ชั่นเดียวสำหรับทรัพยากรที่ถูกล่ามโซ่: ฟังก์ชั่นโรงงานห่อหุ้มการผูกมัด ฉันได้เพิ่มตัวอย่างของการใช้งาน; ขอบคุณ!
Nils von Barth

2

เนื่องจากทรัพยากรของคุณซ้อนกันส่วนคำสั่งลองด้วยของคุณควรเป็น:

try (FileWriter fw=new FileWriter(file)) {
    try (BufferedWriter bw=new BufferedWriter(fw)) {
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }
} catch (IOException ex) {
    // handle ex
}

5
นี่คล้ายกับตัวอย่างที่ 2 ของฉันมาก หากไม่มีข้อยกเว้นเกิดขึ้น FileWriter closeจะถูกเรียกสองครั้ง
Natix

0

ฉันจะบอกว่าอย่าใช้ ARM และปิดต่อไปด้วย ใช้วิธีการเช่น

public void close(Closeable... closeables) {
    for (Closeable closeable: closeables) {
       try {
           closeable.close();
         } catch (IOException e) {
           // you can't much for this
          }
    }

}

นอกจากนี้คุณควรพิจารณาเรียกอย่างใกล้ชิดของBufferedWriterที่มันไม่ได้เป็นเพียงการมอบหมายใกล้กับแต่จะล้างข้อมูลบางอย่างเช่นFileWriterflushBuffer


0

วิธีการแก้ปัญหาของฉันคือการทำ refactoring "แยกวิธี" ดังต่อไปนี้:

static AutoCloseable writeFileWriter(FileWriter fw, String txt) throws IOException{
    final BufferedWriter bw  = new BufferedWriter(fw);
    bw.write(txt);
    return new AutoCloseable(){

        @Override
        public void close() throws IOException {
            bw.flush();
        }

    };
}

printToFile สามารถเขียนได้ทั้ง

static void printToFile(String text, File file) {
    try (FileWriter fw = new FileWriter(file)) {
        AutoCloseable w = writeFileWriter(fw, text);
        w.close();
    } catch (Exception ex) {
        // handle ex
    }
}

หรือ

static void printToFile(String text, File file) {
    try (FileWriter fw = new FileWriter(file);
        AutoCloseable w = writeFileWriter(fw, text)){

    } catch (Exception ex) {
        // handle ex
    }
}

สำหรับนักออกแบบคลาส lib ฉันจะแนะนำให้พวกเขาขยายAutoClosableส่วนต่อประสานด้วยวิธีการเพิ่มเติมเพื่อระงับการปิด ในกรณีนี้เราสามารถควบคุมพฤติกรรมการปิดได้ด้วยตนเอง

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

UPDATE

แต่เดิมโค้ดข้างต้นต้อง@SuppressWarningตั้งแต่ภายในฟังก์ชั่นที่จำเป็นต้องBufferedWriterclose()

ตามที่แนะนำโดยความคิดเห็นหากflush()จะถูกเรียกก่อนปิดตัวเขียนเราจำเป็นต้องทำก่อนคำสั่งใด ๆreturn(โดยนัยหรือชัดเจน) ภายในบล็อกลอง ขณะนี้ยังไม่มีวิธีที่จะทำให้แน่ใจว่าผู้โทรทำสิ่งนี้ฉันคิดว่าจะต้องมีการบันทึกwriteFileWriterไว้

อัพเดทอีกครั้ง

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

ดังนั้นในการแก้ปัญหานี้อย่างถูกต้องเราจำเป็นต้องมีการกำหนดเองAutoClosableว่าเมื่อใดก็ตามที่ปิดการทำงานขีดเส้นใต้BufferedWriterจะถูกflush()แก้ไข ที่จริงแล้วนี่แสดงให้เราเห็นอีกวิธีหนึ่งในการเลี่ยงคำเตือนเนื่องจากBufferWriterไม่เคยปิดในทางใดทางหนึ่ง


คำเตือนมีความหมาย: เราแน่ใจได้หรือไม่ว่าbwจะมีการเขียนข้อมูลออกมาจริงหรือไม่? มันถูกบัฟเฟอร์หลังจากทั้งหมดดังนั้นจึงจำเป็นต้องเขียนลงดิสก์บางครั้งเท่านั้น (เมื่อบัฟเฟอร์เต็มและ / หรือเปิดflush()และclose()วิธีการ) ฉันเดาว่าflush()ควรเรียกวิธีนี้ แต่ก็ไม่จำเป็นต้องใช้ตัวเขียนบัฟเฟอร์แล้วเมื่อเราเขียนมันออกมาทันทีในชุดเดียว และหากคุณไม่แก้ไขรหัสคุณสามารถจบลงด้วยข้อมูลที่เขียนลงไฟล์ในลำดับที่ไม่ถูกต้องหรือไม่ได้เขียนลงไฟล์เลย
Petr Janeček

หากจะต้องเรียกว่ามันจะเกิดขึ้นเมื่อใดก็ตามก่อนที่จะโทรตัดสินใจที่จะปิดflush() FileWriterดังนั้นสิ่งนี้ควรเกิดขึ้นprintToFileก่อนที่จะมีreturnบล็อกลอง สิ่งนี้จะไม่เป็นส่วนหนึ่งของwriteFileWriterการเตือนดังนั้นจึงไม่เกี่ยวกับสิ่งใดในฟังก์ชั่นนั้น แต่เป็นตัวเรียกของฟังก์ชันนั้น หากเรามีคำอธิบายประกอบ@LiftWarningToCaller("wanrningXXX")มันจะช่วยให้กรณีนี้และคล้ายกัน
Earth Engine
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.