2010-02-05 42 views
25

Giao diện java.lang.Iterator có 3 phương pháp: hasNext, nextremove. Để triển khai trình lặp vòng chỉ đọc, bạn phải cung cấp triển khai cho 2 trong số đó: hasNextnext.Việc cần làm của ngoại lệ khi triển khai java.lang.Iterator

Vấn đề của tôi là các phương pháp này không khai báo bất kỳ ngoại lệ nào. Vì vậy, nếu mã của tôi bên trong quá trình lặp lại tuyên bố ngoại lệ, tôi phải kèm theo mã lặp lại của tôi bên trong khối try/catch.

Chính sách hiện tại của tôi đã được tính lại ngoại lệ kèm theo trong RuntimeException. Nhưng điều này có vấn đề bởi vì các trường hợp ngoại lệ được kiểm tra bị mất và mã khách hàng không còn có thể bắt các ngoại lệ đó một cách rõ ràng nữa.

Tôi làm cách nào để giải quyết giới hạn này trong lớp Iterator?

Dưới đây là một số mẫu mã cho rõ ràng:

class MyIterator implements Iterator 
{ 
    @Override 
    public boolean hasNext() 
    { 
     try 
     { 
      return implementation.testForNext(); 
     } 
     catch (SomethingBadException e) 
     { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public boolean next() 
    { 
     try 
     { 
      return implementation.getNext(); 
     } 

     catch (SomethingBadException e) 
     { 
      throw new RuntimeException(e); 
     } 
    } 

    ... 
} 

Trả lời

9

Tôi đã triển khai rất nhiều Iterator, đôi khi trên đầu trang của các trình vòng lặp kiểm tra ngoại lệ (ResultSet là khái niệm một bộ lặp bản ghi, InputStream y khái niệm một trình lặp byte) và vân vân. Nó rất rất đẹp và tiện dụng (bạn có thể thực hiện cấu trúc bộ lọc & ống cho rất nhiều thứ).

Nếu bạn thích khai báo ngoại lệ của bạn, sau đó khai báo một loại Iterator mới (ExceptionIterator, nó sẽ giống như Runnable và Callable). Bạn có thể sử dụng nó cùng hoặc mã của bạn nhưng bạn không thể soạn nó với các thành phần bên ngoài (Thư viện lớp Java hoặc libs bên 3d).

Nhưng nếu bạn thích sử dụng giao diện siêu chuẩn (như trình vòng lặp) để sử dụng chúng ở bất cứ đâu, sau đó sử dụng Iterator. Nếu bạn biết các ngoại lệ của bạn sẽ là một điều kiện để ngừng xử lý của bạn, hoặc bạn không nhớ rất nhiều ... sử dụng chúng.

Trường hợp ngoại lệ thời gian chạy không quá khủng khiếp. Ví dụ. Hibernate sử dụng chúng để thực hiện proxy và các công cụ như thế. Họ phải ngoại trừ DB ngoại lệ nhưng không thể tuyên bố họ trong việc triển khai Danh sách của họ.

+1

Một tùy chọn khác, nếu bạn không thể kiểm soát mã máy khách UI: hãy tạo một trình vòng lặp đăng ký ngoại lệ ở đâu đó bằng bộ thu thập hoặc thứ gì đó hoặc gọi giao diện gọi lại hoặc điều gì đó bạn kiểm soát và hiển thị lỗi ở bất kỳ đâu. Nếu điều đó hữu ích cho bạn, bạn có thể tạo ra một "CatcherIterator" mà chỉ đơn giản là kết thúc một trình khởi động-exception-thrower-iterator và nắm bắt tất cả các ngoại lệ posible chuyển hướng chúng đến giao diện property hoặc callback. – helios

1

Nếu thực hiện của bạn không có các tài sản theo yêu cầu của giao diện Iterator, tại sao bạn muốn sử dụng nó?

Tôi không thấy giải pháp nào khác ngoài RuntimeException (với các sự cố của nó).

11

Bạn nên tính lại ngoại lệ dưới dạng ngoại lệ thời gian chạy tùy chỉnh, không phải là ngoại lệ chung, ví dụ: SomethingBadRuntimeException. Ngoài ra, bạn có thể thử exception tunneling.

Và tôi yên tâm rằng buộc khách hàng phải đối phó với các ngoại lệ bằng cách khiến họ kiểm tra là một hành vi không tốt. Nó chỉ gây ô nhiễm mã hoặc lực của bạn để bọc các ngoại lệ với các mã thời gian chạy hoặc lực lượng xử lý chúng thay cho cuộc gọi nhưng không tập trung. Chính sách của tôi là tránh sử dụng các ngoại lệ đã kiểm tra càng nhiều càng tốt. Hãy xem xét IOException trên Closable.close(): hữu ích hay tiện lợi không? Các trường hợp khi nó rất hiếm, nhưng mọi nhà phát triển Java trên thế giới đều bị buộc phải đối phó với nó. Thông thường nó được nuốt hoặc đăng nhập tốt nhất. Và hãy tưởng tượng số tiền này thêm vào code size! Có một số bài viết về trường hợp ngoại lệ kiểm tra từ mặt tối của họ:

  1. "Does Java need Checked Exceptions?" by Bruce Eckel
  2. The Trouble with Checked Exceptions A Conversation with Anders Hejlsberg, by Bill Venners with Bruce Eckel
  3. Java Design Flaws, C2 wiki

Có những trường hợp khi kiểm tra ngoại lệ đến để giải thoát. Nhưng theo ý kiến ​​của tôi, họ rất hiếm và thường quan tâm đến việc thực hiện một số mô-đun cụ thể. Và họ không mang lại nhiều lợi nhuận.

3

Là một người thích Java kiểm tra ngoại lệ, tôi nghĩ rằng vấn đề (Java thiết kế lỗ hổng nếu bạn sẽ) là các thư viện chuẩn không hỗ trợ một loại Iterator generic nơi phương pháp next ném một ngoại lệ kiểm tra.

Cơ sở mã cho Sesame có lớp biến thể Iterator được gọi là Iterable chỉ thực hiện điều đó. Chữ ký như sau:

 
Interface Iteration<E,X extends Exception> 

Type Parameters: 
    E - Object type of objects contained in the iteration. 
    X - Exception type that is thrown when a problem occurs during iteration. 

Điều này dường như làm việc trong bối cảnh hạn chế về Sesame, nơi lớp con cụ thể được sử dụng mà "sửa chữa" các tham số kiểu ngoại lệ. Nhưng (tất nhiên) nó không tích hợp với các loại bộ sưu tập tiêu chuẩn, cú pháp vòng lặp mới for của Java 5, v.v.

2

hasNext không thực sự ném ngoại lệ - nên kiểm tra xem có ổn không để tiếp tục và trả về giá trị boolean trên cơ sở đó (tôi sẽ ghi lại bất kỳ ngoại lệ nào có trình ghi nhật ký). Tôi sẽ ném một RuntimeException nếu tiếp theo thất bại (và đăng nhập ngoại lệ kiểm tra với một logger). Tiếp theo không nên thất bại trong điều kiện bình thường nếu thử nghiệm là ok và nếu thử nghiệm không thành công bạn không nên gọi tiếp theo (do đó ngoại lệ thời gian chạy).

1

Câu hỏi này đã được hỏi một thời gian dài trước đây, nhưng tôi muốn có phản hồi về một mô hình có thể hữu ích đây. Một giao diện bổ sung hoặc lớp ExceptionHandler có thể được tạo thành trong lớp mà triển khai Iterator và có thể được thực hiện bởi máy khách để giải quyết các ngoại lệ như mong muốn.

public interface ExceptionHandler { 
    // Log, or rethrow, or ignore... 
    public Object handleException(Exception e); 
} 

lớp Thực hiện có thể làm một cái gì đó như thế này:

public class ExceptionRethrower implements ExceptionHandler { 
    @Override 
    public Object handleException(Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

public class ExceptionPrinter implements ExceptionHandler { 
    @Override 
    public Object handleException(Exception e) { 
     System.err.println(e); 
     return e; 
    } 
} 

Đối với một iterator mà đọc từ một tập tin, các mã có thể giống như thế này:

public class MyReaderIterator implements Iterator<String> { 
    private final BufferedReader reader; 
    private final ExceptionHandler exceptionHandler; 
    public MyReaderIterator(BufferedReader r, ExceptionHandler h) { 
     reader = r; 
     exceptionHandler = h; 
    } 
    @Override 
    public String next() { 
     try { 
      return reader.readLine(); 
     } catch (IOException ioe) { 
      Object o = exceptionHandler.handleException(ioe); 
      // could do whatever else with o 
      return null; 
     } 
    } 
    // also need to implement hasNext and remove, of course 
} 

nào để mọi người nghĩ gì ? Nó là giá trị mức độ này của indirection, hoặc nó là một vi phạm phân lớp và chỉ đơn giản là quá phức tạp? Tên giao diện có thể không chính xác thích hợp (có lẽ ExceptionProcessor hoặc ExceptionInterceptor vì nó có thể tương tác nhưng không xử lý hoàn toàn ngoại lệ, vẫn cần phải xử lý một số trong lớp mà nó được tạo).

Tôi đồng ý rằng tất cả điều này dường như chỉ là một giải pháp cho thực tế là Iterator.next() không được khai báo để ném ngoại lệ, và làm cho tôi thông cho máy phát điện và mô hình ngoại lệ của python.

0

tôi usally sử dụng

@Override 
void remove() { 
    throws new UnsupportedOperationException("remove method not implemented"); 
} 

lưu ý: UnsupportedOperationException là một RuntimeException

Vì bạn đang properbly hướng tới một cái gì đó giống như

for (IterElem e : iterable) 
    e.action(); 

Bạn sẽ ổn thôi

1

Nếu bạn thực sự không muốn sử dụng ngoại lệ thời gian chạy, bạn có thể sử dụng NoSuchElementException. Bạn được phép sử dụng nó trong phương pháp next() bị ghi đè vì nó được khai báo để được ném theo định dạng Iterator (Tôi đã thử cách này và mã được biên dịch). Nhưng bạn nên cân nhắc xem có phải là ngữ nghĩa hợp lệ cho trường hợp của bạn hay không.

+0

Tôi nghĩ rằng đây là ngoại lệ tốt nhất để ném cho các lỗi iterator – smac89

+0

'NoSuchElementException' là một' RuntimeException'. Bạn cũng có thể tạo phần mở rộng tùy chỉnh của riêng bạn 'RuntimeException', nó sẽ giống nhau, phải không? – boumbh

-1

Trong tâm trí của tôi, vòng lặp có nghĩa là để lặp, họ không có nghĩa là tính toán, do đó sự vắng mặt của throws vào phương pháp next().

Bạn đang hỏi cách sử dụng giao diện để thực hiện điều gì đó mà nó không được thiết kế cho.

Tuy nhiên, nếu Iterator là nhằm để được sử dụng một mình (mà nói chung là một giả định xấu), bạn có thể xác định một phương pháp nào safeNext() như thế này:

public Element safeNext() throws YourCheckedException { 
    ... 
} 

@Override 
public Element next() { 
    try { 
     return safeNext(); 
    } catch (YourException e) { 
     throw new YourRuntimeException("Could not fetch the next element", e); 
    } 
} 

Sau đó, khi sử dụng Iterator bạn có thể chọn sử dụng safeNext() và xử lý ngoại lệ đã kiểm tra hoặc sử dụng next() như bạn làm với bất kỳ trình lặp nào.

Tóm lại, bạn không thể có cả sự tiện lợi của giao diện trình lặp (bạn không thể mong đợi rằng đối tượng Iterable sẽ sử dụng phương thức safeNext()) và trường hợp ngoại lệ đã chọn.

Các vấn đề liên quan