2015-04-24 16 views
8

Tôi đang chơi xung quanh với cách viết một tập hợp các đối tượng vào một tập tin. Tại sao việc triển khai dưới đây sử dụng Iterable.forEach() không biên dịch? Trong Eclipse, tôi nhận được thông báo rằng một IOException không được xử lý. Điều này đặc biệt khó hiểu vì tôi dường như đang xử lý IOExceptions.Làm thế nào để xử lý IOException trong Iterable.forEach?

public void write(Iterable<?> objects) { 
    try (BufferedWriter bw = new BufferedWriter(
     new OutputStreamWriter(
      new FileOutputStream("out.txt"), "UTF-8"));) { 

     objects.forEach((o) -> bw.write(o.toString())); //Unhandled exception type IOException 

    } catch (IOException e) { 
    //handle exception 
    } 
} 

Rõ ràng, các công trình bên dưới. Tôi quan tâm đến lý do tại sao ở trên không hoạt động và cách khắc phục.

for (Object o : objects) { bw.write(o.toString()); } 

Tôi đã kiểm tra các tài liệu ConsumerIterable, và không ai trong số họ dường như cho thấy làm thế nào để giải quyết việc này.

+0

Bạn có thể biên dịch nó thông qua dòng lệnh không? Phiên bản nhật thực nào? – javajavajava

Trả lời

9

Tiền thân: The Catch or Specify Requirement.

Chúng ta hãy viết lambda như một lớp vô danh:

objects.forEach(new Consumer<Object>() { 
    public void accept(Object o) { 
     bw.write(o.toString()); 
    } 
}); 

chúng tôi đang xử lý nó? Rõ ràng là chúng ta không.

Khi chúng tôi viết biểu thức lambda, chúng tôi khai báo phần thân của phương thức. Chúng tôi cũng không thể khai báo mệnh đề throws cho lambda.

Các "con đường xung quanh nó" duy nhất là làm một cái gì đó như sau:

try (BufferedWriter bw = new BufferedWriter(
    new OutputStreamWriter(
     new FileOutputStream("out.txt"), "UTF-8"));) { 

    objects.forEach((o) -> { 
     try { 
      bw.write(o.toString())); 
     } catch(IOException kludgy) { 
      throw new RuntimeException(kludgy); 
     } 
    }); 

} catch (RuntimeException kludgy) { 
    Throwable cause = kludgy.getCause(); 
    if (cause instanceof IOException) { 
     IOException e = (IOException) cause; 
     // handle exception 
    } else { 
     throw kludgy; 
    } 
} 

Nhưng điều này thực sự là không tốt.

Thay vào đó, không sử dụng forEach trong ngữ cảnh ném ngoại lệ đã kiểm tra hoặc nắm bắt ngoại lệ trong phần thân của lambda.

+0

Đó là những gì tôi đang tìm kiếm. Cảm ơn! –

2

Vấn đề là phương pháp accept trong giao diện Consumer không được khai báo để ném ngoại lệ. Vì vậy, bạn không thể sử dụng một phương thức ném một ngoại lệ đã kiểm tra trong lambda.

Giải pháp là sử dụng một cho mỗi vòng lặp thay thế.

+1

Than ôi, đây là một trong những thiếu sót của việc triển khai lambda hiện tại mà tôi đã đập đầu vào nhiều lần! :) –

6

Nếu tất cả những gì bạn quan tâm là in chuỗi vào một tệp, hãy sử dụng PrintStream hoặc có lẽ PrintWriter thay vì các lớp khác Writer. Tính năng đáng chú ý của PrintStreamPrintWriter là hoạt động in của chúng không được ném IOException. Họ cũng gọi toString trên các đối tượng tự động, mà làm những điều rất thuận lợi:

public void write1(Iterable<?> objects) { 
    try (PrintStream ps = new PrintStream("printout.txt", "UTF-8")) { 
     objects.forEach(ps::println); 
    } catch (IOException ioe) { 
     // handle 
    } 
} 

Nếu bạn lo ngại về lỗi, bạn có thể gọi PrintStream.checkError, mặc dù điều này không cho bạn bất kỳ chi tiết cụ thể về bất cứ lỗi nào có thể xảy ra .

Câu hỏi chung vẫn là viết tắt, tuy nhiên, về việc cần làm nếu bạn muốn gọi phương thức ném ngoại lệ từ trong ngữ cảnh (chẳng hạn như forEach) không cho phép. Điều này chỉ gây phiền nhiễu để đối phó với, mặc dù chỉ vừa phải như vậy. Nó đòi hỏi một số thiết lập, tuy nhiên. Giả sử chúng ta muốn viết Consumer để ném một số IOException.Chúng ta phải tuyên bố giao diện chức năng riêng của chúng tôi:

interface IOConsumer<T> { 
    void accept(T t) throws IOException; 
} 

Bây giờ chúng ta cần phải viết một hàm có thể chuyển đổi một IOConsumer đến một Consumer. Nó thực hiện điều này bằng cách chuyển đổi bất kỳ IOException nó bắt vào một UncheckedIOException, một ngoại lệ được tạo ra cho mục đích này.

static <T> Consumer<T> wrap(IOConsumer<? super T> ioc) { 
    return t -> { 
     try { 
      ioc.accept(t); 
     } catch (IOException ioe) { 
      throw new UncheckedIOException(ioe); 
     } 
    }; 
} 

Với những tại chỗ, bây giờ chúng ta có thể viết lại ví dụ ban đầu như sau:

public void write2(Iterable<?> objects) { 
    try (BufferedWriter bw = new BufferedWriter(
      new OutputStreamWriter(
       new FileOutputStream("out.txt"), "UTF-8"))) { 
     objects.forEach(wrap(o -> bw.write(o.toString()))); 
    } catch (IOException|UncheckedIOException e) { 
     //handle exception 
    } 
} 
1

thường chúng tôi không ghi dữ liệu vào tập tin nếu có ngoại lệ occures. giữ điều này trong tâm trí chúng ta có thể viết own consumer which will wrap the checked exception into an unchecked exception của chúng tôi. theo cách này, bạn có thể thoát khỏi lỗi thời gian biên dịch. vui lòng thử bên dưới mã

import java.io.*; 
import java.util.*; 
public class HelloWorld{ 

    public static void main(String []args){ 
     write(Arrays.asList(1,2,3)); 
    } 

    public static void write(Iterable<?> objects) { 
    try (BufferedWriter bw = new BufferedWriter(
     new OutputStreamWriter(
      new FileOutputStream("c:\\out.txt"), "UTF-8"))) { 

     objects.forEach(o->myConsumerThrowsRuntimeException(bw,o.toString())); //Unhandled exception type IOException 

    } catch (Exception e) { 
    //handle exception 
    } 
    } 
    public static void myConsumerThrowsRuntimeException(Writer writer , String data){ 
     try{ 
      writer.write(data); 
     }catch(IOException e){ 

      throw new RuntimeException("Cannot write Data error is "+e.getMessage()); 
     } 
    } 
} 
Các vấn đề liên quan