Java 8: ฉันจะทำงานกับเมธอดการโยนข้อยกเว้นในสตรีมได้อย่างไร


172

สมมติว่าฉันมีชั้นเรียนและวิธีการ

class A {
  void foo() throws Exception() {
    ...
  }
}

ตอนนี้ฉันต้องการเรียก foo สำหรับแต่ละอินสแตนซ์ที่Aส่งโดยสตรีมเช่น:

void bar() throws Exception {
  Stream<A> as = ...
  as.forEach(a -> a.foo());
}

คำถาม: ฉันจะจัดการข้อยกเว้นได้อย่างถูกต้องได้อย่างไร? รหัสไม่ได้รวบรวมในเครื่องของฉันเพราะฉันไม่จัดการข้อยกเว้นที่เป็นไปได้ที่สามารถโยนโดย foo () throws Exceptionของbarดูเหมือนว่าจะไร้ประโยชน์ที่นี่ ทำไมถึงเป็นอย่างนั้น?



ที่เกี่ยวข้อง: stackoverflow.com/q/30117134/435605
AlikElzin-kilaka

คำตอบ:


141

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

สำนวนห่อปกติคืออะไร:

private void safeFoo(final A a) {
    try {
        a.foo();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

(ยกเว้น supertype Exceptionเป็นเพียงใช้เป็นตัวอย่างที่ไม่เคยพยายามที่จะจับมันด้วยตัวเอง)

as.forEach(this::safeFoo)จากนั้นคุณสามารถเรียกมันว่าด้วย:


1
หากคุณต้องการให้มันเป็นวิธีการห่อหุ้มฉันจะประกาศว่าคงที่ มันไม่ได้ใช้อะไรจาก 'นี่'
aalku

206
มันเป็นเรื่องน่าเศร้าที่เราต้องทำเช่นนี้แทนที่จะใช้ข้อยกเว้นในท้องถิ่นของเรา .... โอ้ชวามันให้แล้วนำไป
Erich

1
@Stephan คำตอบนั้นถูกลบ แต่ก็ยังมีอยู่ที่นี่: stackoverflow.com/a/27661562/309308
Michael Mrozek

3
นั่นจะทำให้การตรวจสอบโค้ดใน บริษัท ของฉันล้มเหลวทันที: เราไม่ได้รับอนุญาตให้โยนข้อยกเว้นที่ไม่ได้ตรวจสอบ
Stelios Adamantidis

7
@SteliosAdamantidis กฎนั้นมีข้อ จำกัด อย่างไม่น่าเชื่อและให้ผลตอบโต้ที่ดี คุณทราบหรือไม่ว่าวิธีการทั้งหมดใน API ทั้งหมดได้รับอนุญาตให้ทิ้งข้อยกเว้นรันไทม์รสชาติทั้งหมดโดยที่คุณไม่ต้องรู้ (แน่นอนว่าคุณเป็น) คุณห้าม javascript ที่ไม่มีแนวคิดเกี่ยวกับข้อยกเว้นที่ตรวจสอบแล้วหรือไม่ หากฉันเป็นผู้พัฒนานำของคุณฉันจะห้ามตรวจสอบข้อยกเว้นแทน
spi

35

หากสิ่งที่คุณต้องการคือการเรียกใช้fooและคุณต้องการเผยแพร่ข้อยกเว้นตามที่เป็น (โดยไม่ต้องล้อมรอบ) คุณสามารถใช้การforวนซ้ำของ Java แทน (หลังจากเปลี่ยน Stream ให้เป็น Iterable ด้วยกลอุบาย ):

for (A a : (Iterable<A>) as::iterator) {
   a.foo();
}

นี่คืออย่างน้อยสิ่งที่ฉันทำในการทดสอบ JUnit ของฉันที่ฉันไม่ต้องการผ่านปัญหาของการห่อข้อยกเว้นที่ตรวจสอบของฉัน


16

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

วิธีที่ดีที่สุดในความคิดของฉันคุณสามารถหาได้รับจาก Misha ที่นี่ ข้อยกเว้นรันไทม์รวมใน Java 8 สตรีม โดยเพียงแค่ดำเนินการใน "ฟิวเจอร์" ดังนั้นคุณสามารถเรียกใช้ส่วนการทำงานทั้งหมดและรวบรวมข้อยกเว้นที่ไม่ทำงานเป็นชิ้นเดียว มิฉะนั้นคุณสามารถรวบรวมพวกเขาทั้งหมดในรายการและดำเนินการในภายหลัง

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

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

หากคุณไม่ชอบวิธีใด ๆ เหล่านี้ให้ลองใช้ (ขึ้นอยู่กับข้อยกเว้นดั้งเดิม) อย่างน้อยก็เป็นข้อยกเว้นของตัวเอง


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

2
คุณควรเลือกระดับที่คุณต้องการจับพวกเขาและสตรีมที่ยุ่งเหยิง Stream API ควรให้คุณมีข้อยกเว้นจนกว่าการดำเนินการขั้นสุดท้าย (เช่นการรวบรวม) และได้รับการจัดการที่นั่นด้วยตัวจัดการหรือถูกโยนทิ้งเป็นอย่างอื่น stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...). มัน sholud คาดหวังข้อยกเว้นและช่วยให้คุณจัดการกับพวกเขาเหมือนฟิวเจอร์สทำ
aalku

10

ฉันแนะนำให้ใช้คลาส Google Guava Throwables

เผยแพร่ ( Throwable Throwable)

เผยแพร่ Throwable ตามที่เป็นอยู่หากเป็นอินสแตนซ์ของ RuntimeException หรือข้อผิดพลาดหรืออย่างอื่นเป็นทางเลือกสุดท้ายห่อใน RuntimeException แล้วจึงเผยแพร่ **

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            throw Throwables.propagate(e);
        }
    });
}

UPDATE:

ตอนนี้มันถูกเลิกใช้:

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    });
}

4
วิธีนี้เลิกใช้แล้ว (น่าเสียดาย)
Robert Važan

9

คุณสามารถห่อและแกะข้อยกเว้นด้วยวิธีนี้

class A {
    void foo() throws Exception {
        throw new Exception();
    }
};

interface Task {
    void run() throws Exception;
}

static class TaskException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public TaskException(Exception e) {
        super(e);
    }
}

void bar() throws Exception {
      Stream<A> as = Stream.generate(()->new A());
      try {
        as.forEach(a -> wrapException(() -> a.foo())); // or a::foo instead of () -> a.foo()
    } catch (TaskException e) {
        throw (Exception)e.getCause();
    }
}

static void wrapException(Task task) {
    try {
        task.run();
    } catch (Exception e) {
        throw new TaskException(e);
    }
}

9

คุณอาจต้องการทำอย่างใดอย่างหนึ่งต่อไปนี้:

  • เผยแพร่ข้อยกเว้นตรวจสอบ
  • ล้อมรอบและเผยแพร่ข้อยกเว้นที่ไม่ได้ตรวจสอบหรือ
  • ตรวจจับข้อยกเว้นและหยุดการเผยแพร่

ห้องสมุดหลายแห่งให้คุณทำอย่างง่ายดาย ตัวอย่างด้านล่างเขียนโดยใช้ห้องสมุดNoExceptionของฉัน

// Propagate checked exception
as.forEach(Exceptions.sneak().consumer(A::foo));

// Wrap and propagate unchecked exception
as.forEach(Exceptions.wrap().consumer(A::foo));
as.forEach(Exceptions.wrap(MyUncheckedException::new).consumer(A::foo));

// Catch the exception and stop propagation (using logging handler for example)
as.forEach(Exceptions.log().consumer(Exceptions.sneak().consumer(A::foo)));

เก่งมากในห้องสมุด! ฉันกำลังจะเขียนสิ่งที่คล้ายกัน
Jasper de Vries

2

วิธีการอ่านเพิ่มเติม:

class A {
  void foo() throws MyException() {
    ...
  }
}

เพียงซ่อนไว้ในRuntimeExceptionเพื่อที่จะผ่านมันไปforEach()

  void bar() throws MyException {
      Stream<A> as = ...
      try {
          as.forEach(a -> {
              try {
                  a.foo();
              } catch(MyException e) {
                  throw new RuntimeException(e);
              }
          });
      } catch(RuntimeException e) {
          throw (MyException) e.getCause();
      }
  }

แม้ว่าจะมาถึงจุดนี้ฉันจะไม่จับใครบางคนถ้าพวกเขาพูดว่าข้ามลำธารและไปด้วยลูปเว้นแต่ว่า:

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