Một số người cố gắng thuyết phục bạn rằng bạn phải chơi đúng luật. Nghe, nhưng bạn có tuân theo hay không, bạn nên tự quyết định tùy thuộc vào hoàn cảnh của bạn. Thực tế là "bạn NÊN chơi theo luật" (không phải "bạn PHẢI chơi theo luật"). Chỉ cần lưu ý rằng nếu bạn không chơi đúng luật, có thể có hậu quả.
Tình huống này không chỉ áp dụng trong tình huống Runnable
, mà với Java 8 cũng rất thường xuyên trong bối cảnh Luồng và những nơi khác mà các giao diện chức năng đã được giới thiệu mà không có khả năng xử lý các ngoại lệ đã kiểm tra. Ví dụ, Consumer
, Supplier
, Function
, BiFunction
và như vậy có tất cả được công bố mà không cần cơ sở vật chất để đối phó với trường hợp ngoại lệ kiểm tra.
Vậy các tình huống và lựa chọn là gì? Trong văn bản dưới đây, Runnable
là đại diện của bất kỳ giao diện chức năng nào không khai báo ngoại lệ hoặc tuyên bố ngoại lệ quá giới hạn cho trường hợp sử dụng hiện tại.
- Bạn đã tự khai báo
Runnable
ở đâu đó và có thể thay thế Runnable
bằng thứ khác.
- Xem xét thay thế
Runnable
bằng Callable<Void>
. Về cơ bản là cùng một thứ, nhưng được phép ném các ngoại lệ; và return null
cuối cùng, đó là một sự khó chịu nhẹ.
- Cân nhắc thay thế
Runnable
bằng tùy chỉnh của riêng bạn @FunctionalInterface
để có thể loại bỏ chính xác những ngoại lệ mà bạn muốn.
- Bạn đã sử dụng một API và có sẵn các lựa chọn thay thế. Ví dụ: một số API Java bị quá tải nên bạn có thể sử dụng
Callable<Void>
thay thế Runnable
.
- Bạn đã sử dụng một API và không có lựa chọn thay thế nào. Trong trường hợp đó, bạn vẫn chưa hết lựa chọn.
- Bạn có thể bọc ngoại lệ vào
RuntimeException
.
- Bạn có thể hack ngoại lệ vào RuntimeException bằng cách sử dụng một diễn viên không được kiểm tra.
Bạn có thể thử những cách sau. Đó là một chút hack, nhưng đôi khi hack là thứ chúng ta cần. Bởi vì, việc một ngoại lệ nên được kiểm tra hay bỏ chọn được xác định bởi kiểu của nó, nhưng trên thực tế, thực tế nên được xác định theo tình huống.
@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
@Override
default void run() {
try {
tryRun();
} catch (final Throwable t) {
throwUnchecked(t);
}
}
private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
throw (E) t;
}
void tryRun() throws Throwable;
}
Tôi thích điều này hơn new RuntimeException(t)
vì nó có dấu vết ngăn xếp ngắn hơn.
Bây giờ bạn có thể làm:
executorService.submit((ThrowingRunnable) () -> {throw new Exception()});
Tuyên bố từ chối trách nhiệm: Khả năng thực hiện các phôi không được kiểm tra theo cách này có thể thực sự bị loại bỏ trong các phiên bản Java trong tương lai, khi thông tin kiểu generics được xử lý không chỉ tại thời điểm biên dịch mà còn trong thời gian chạy.