2009-01-19 22 views
5

Trong java một lớp có thể thực hiện Iterable cho phép bạn sử dụng câu lệnh foreach() và lặp đường syntatic:Trong Java, làm thế nào bạn sẽ viết tương đương với Iterable mà có thể ném ngoại lệ?

for(T t:ts) ... 

Tuy nhiên, điều này không cho phép bạn ném ngoại lệ về việc xây dựng cho một Iterator. Nếu bạn đang lặp lại một mạng, tập tin, cơ sở dữ liệu, vv nó sẽ được tốt đẹp để có thể ném ngoại lệ. Các ứng cử viên rõ ràng là java.io.InputStream, Reader và mã java.nio.Channel, nhưng không ai trong số này có thể sử dụng Generics như giao diện Iterable có thể.

Có một thành ngữ chung hoặc API Java cho tình huống này không?

Làm rõ: Điều này hỏi xem có giao diện mẫu hay giao diện thay thế để lặp cho các đối tượng ngoài nguồn không nhớ. Theo phản ứng đã nói, chỉ cần ném RuntimeExceptions để có được xung quanh vấn đề không được đề nghị hoặc những gì tôi đang tìm kiếm.

Chỉnh sửa 2: Cảm ơn câu trả lời cho đến nay. Sự đồng thuận dường như là "bạn không thể". Vì vậy, tôi có thể mở rộng câu hỏi cho "Bạn làm gì trong tình huống này, khi tình huống này hữu ích?" Chỉ cần viết giao diện của riêng bạn?

Trả lời

4

Thật không may là bạn không thể. Có hai vấn đề:

  • Các Iterator API không tuyên bố bất kỳ trường hợp ngoại lệ được ném, vì vậy bạn sẽ phải ném RuntimeExceptions (hoặc throwables không ngoại lệ)
  • Các tăng cường cho vòng lặp không làm bất kỳ điều gì để cố gắng giải phóng tài nguyên ở cuối vòng lặp

Điều này rất khó chịu. Trong C#, ví dụ, bạn có thể thực sự dễ dàng viết mã để lặp qua các dòng của một tập tin văn bản:

public static IEnumerable<string> ReadLines(string filename) 
{ 
    using (TextReader reader = File.OpenText(filename)) 
    { 
     string line; 
     while ((line=reader.ReadLine()) != null) 
     { 
      yield return line; 
     } 
    } 
} 

Use as:

foreach (string line in ReadLines("foo.txt")) 

Vòng lặp foreach gọi Dispose trên IEnumerator trong một cuối cùng khối, mà dịch để "kiểm tra nếu chúng ta cần phải làm bất cứ điều gì trong khối của iterator cuối cùng (từ báo cáo sử dụng)". Rõ ràng là không có ngoại lệ nào được kiểm tra trong C#, do đó khía cạnh của mọi thứ cũng không phải là vấn đề.

Một thành ngữ (hữu ích!) Hoàn toàn không hoạt động được trong Java do điều này.

0

Tôi muốn nói rằng bạn không thể, ngay cả khi bạn có thể bạn có lẽ không nên. Bạn nhận được byte từ những thứ này, nếu chúng được sử dụng trong một vòng lặp for có khả năng mỗi byte sẽ kết thúc đóng hộp.

Những gì bạn có thể làm là các ngoại lệ được kiểm tra trong trường hợp ngoại lệ không được kiểm tra và tuân thủ giao diện có thể lặp lại, mặc dù điều này không được khuyến khích.

4

Các luồng như mạng không thực sự lặp lại theo nghĩa truyền thống. Dữ liệu có thể đi qua bất cứ lúc nào, do đó, nó không có ý nghĩa để có một cho mỗi vòng lặp.

Để đọc tệp hoặc chụp nhanh DB (như truy vấn chọn), bạn không thể lấy dữ liệu đó, phân đoạn thành các khối logic và triển khai giao diện có thể lặp lại.

Bạn cũng có thể gọi phương thức khởi tạo trước tiên sẽ bắt bất kỳ ngoại lệ nào, nếu đó là vấn đề.

try{ 
    ts.initializeIOIterator(); 
}catch(...) 

for(T t:ts) 
...  
+0

Cảm ơn câu trả lời. Nhưng nếu bạn gọi ban đầu trước tiên bạn phải đọc tất cả mọi thứ vào bộ nhớ trước, và mất lợi ích của streaming, hoặc bạn phải đối phó với ngoại lệ –

+0

Yup. Bạn khá nhiều phải chọn một ẩn dụ hoặc khác. Nếu bạn đang đối phó với một luồng thật, thì bạn nên gắn bó với phép ẩn dụ của luồng. – patros

1

Điều tốt nhất bạn có thể làm là tạo RuntimeIOException mà bạn sẽ ném từ triển khai hasNext/tiếp theo của bạn trong trường hợp lỗi.

try { 
    for (...) { 
    // do my stuff here 
    } 
catch (RuntimeIOException e) { 
    throw e.getCause(); // rethrow IOException 
} 

RuntimeIOException sẽ runtime ngoại lệ, gói IOException của bạn:

class RuntimeIOException extends RuntimeException { 
    RuntimeIOException(IOException e) { 
    super(e); 
    } 

    IOException getCause() { 
    return (IOException) super.getCause(); 
    } 
} 

Đôi khi không có cách nào khác.

0

Nói chung trong trường hợp này, tôi sẽ ném một lớp con thích hợp của RuntimeException trong quá trình triển khai của Iterable.

Về cách xóa tài nguyên, khối try - finally hoạt động cũng như gói một khối foreach giống như bất kỳ mã bit nào khác, do đó, từ quan điểm của khách hàng, nó có thể dễ dàng sử dụng để dọn sạch mọi tài nguyên. Nếu bạn muốn quản lý tài nguyên trong Iterable nó có thể phức tạp hơn, vì không có điểm bắt đầu và kết thúc vòng đời hoàn toàn rõ ràng.

Trong trường hợp này tốt nhất mà bạn có thể có thể làm là để tạo ra các nguồn lực theo yêu cầu (tức là cuộc gọi đầu tiên để next()), và sau đó tiêu diệt chúng hoặc khi một cuộc gọi đến next() sắp trở false, hoặc khi một ngoại lệ được ném vào phần thân của next(). Tất nhiên, điều này có nghĩa là khi phương thức next() của bạn thoát ra ngoài với một ngoại lệ, trình vòng lặp không thể được sử dụng nữa - đây không phải là một ràng buộc không hợp lý (xem xét ngoại lệ một phiên bản lỗi hơn là trả về false). một cái gì đó bạn nên tài liệu vì điều này không được bao phủ bởi giao diện.

Điều đó nói rằng, ở trên giả định rằng bạn đang tạo một cái gì đó chỉ là một Iterable. Tôi thấy rằng trong thực tế, khi tôi triển khai Iterable trên một lớp, nó giống như một "super-getter" (tức là cách để khách hàng truy cập thông tin được lưu trữ một cách thuận tiện), hơn là điểm của chính lớp đó. Hầu hết thời gian các đối tượng này sẽ được thiết lập độc lập và truy cập thông qua các phương pháp khác, vì vậy vòng đời của chúng có thể được quản lý hoàn toàn riêng biệt với sự tồn tại của chúng như là một Iterable.

Điều này có vẻ tiếp tuyến với câu hỏi, nhưng câu trả lời ngay cho câu hỏi là đơn giản ("sử dụng ngoại lệ thời gian chạy") - phần khó khăn là duy trì trạng thái thích hợp khi có các ngoại lệ này.

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