Đây là một mẹo kỳ lạ mà bạn có thể sử dụng để cải thiện việc xử lý ngoại lệ của mình.
Hãy nói rằng chức năng ánh xạ của bạn là như thế này:
String doMap(Object obj) {
if (isInvalid(obj)) {
throw new IllegalArgumentException("info about obj");
} else {
return obj.toString();
}
}
này trả về kết quả là nếu đối tượng có giá trị, nhưng nó ném một ngoại lệ nếu đối tượng không hợp lệ. Thật không may nếu bạn dính trực tiếp vào một đường ống, bất kỳ lỗi nào cũng sẽ dừng việc thực thi đường ống. Những gì bạn muốn là một cái gì đó giống như một loại "hoặc" có thể giữ một giá trị hoặc một chỉ báo lỗi (mà sẽ là một ngoại lệ trong Java).
Chỉ ra rằng CompletableFuture
có thể chứa giá trị hoặc ngoại lệ. Mặc dù nó được thiết kế để xử lý không đồng bộ - điều này không xảy ra ở đây - chúng tôi chỉ phải sử dụng nó một chút để sử dụng cho mục đích của chúng tôi.
Đầu tiên, cho một stream
của các đối tượng để xử lý, chúng ta gọi hàm lập bản đồ được bọc trong một cuộc gọi đến supplyAsync
:
CompletableFuture<String>[] cfArray =
stream.map(obj -> CompletableFuture.supplyAsync(() -> doMap(obj), Runnable::run))
.toArray(n -> (CompletableFuture<String>[])new CompletableFuture<?>[n]);
(Thật không may, việc tạo mảng chung đưa ra một cảnh báo không được kiểm soát, trong đó sẽ phải đàn áp.)
các cấu trúc kỳ lạ
CompletableFuture.supplyAsync(supplier, Runnable::run)
chạy các nhà cung cấp "không đồng bộ" trên Executor cung cấp Runnable::run
, mà chỉ đơn giản là chạy nhiệm vụ ngay lập tức trong chủ đề này. Nói cách khác, nó vận hành đồng bộ nhà cung cấp.
Bí quyết là trường hợp CompletableFuture
được trả lại từ cuộc gọi này chứa giá trị từ nhà cung cấp, nếu nó trả về bình thường hoặc nó có ngoại lệ, nếu nhà cung cấp đã ném một. (Tôi bỏ qua việc hủy bỏ ở đây.) Sau đó chúng tôi thu thập các trường hợp CompletableFuture
vào một mảng. Tại sao một mảng? Thiết lập của nó cho phần tiếp theo:
CompletableFuture.allOf(cfArray).join();
Điều này thường chờ cho mảng CF hoàn tất.Vì chúng đã được chạy đồng bộ, nên tất cả chúng đã hoàn tất. Điều quan trọng đối với trường hợp này là join()
sẽ ném một số CompletionException
nếu bất kỳ của CF nào trong mảng đã hoàn thành đặc biệt. Nếu tham gia hoàn thành bình thường, chúng ta chỉ có thể thu thập các giá trị trả lại. Nếu tham gia ném một ngoại lệ, chúng ta có thể truyền bá nó, hoặc chúng ta có thể bắt nó và xử lý các ngoại lệ được lưu trữ trong các CF trong mảng đó. Ví dụ:
try {
CompletableFuture.allOf(cfArray).join();
// no errors
return Arrays.stream(cfArray)
.map(CompletableFuture::join)
.collect(toList());
} catch (CompletionException ce) {
long errcount =
Arrays.stream(cfArray)
.filter(CompletableFuture::isCompletedExceptionally)
.count();
System.out.println("errcount = " + errcount);
return Collections.emptyList();
}
Nếu tất cả đều thành công, điều này sẽ trả về danh sách các giá trị. Nếu có bất kỳ ngoại lệ nào, điều này sẽ tính số lượng ngoại lệ và trả về một danh sách trống. Tất nhiên, bạn có thể dễ dàng làm điều gì đó khác, như ghi lại các chi tiết về trường hợp ngoại lệ, lọc ra các trường hợp ngoại lệ và trả về một danh sách các giá trị hợp lệ vv
Nếu bạn không muốn trả lại bất cứ điều gì khi một ngoại lệ được ném, có lẽ bạn nên sử dụng 'flatmap' thay vì' map'. – Balduz
Có lẽ bạn chỉ có thể trả về null thay vì một String và lọc trên các đối tượng không null? – Davio
@membersound, là 'ParseException' một cái gì đó bạn tự tạo ra, hoặc' java.text.ParseException'? – aioobe