สร้างประเภทส่งคืนแบบกำหนดเองที่จะเผยแพร่ข้อยกเว้นที่ตรวจสอบ นี่เป็นอีกทางเลือกหนึ่งในการสร้างอินเทอร์เฟซใหม่ที่สะท้อนถึงส่วนต่อประสานการทำงานที่มีอยู่ด้วยการปรับเปลี่ยนเล็กน้อยของ "throws exception" ในวิธีการของส่วนต่อประสานการทำงาน
คำนิยาม
CheckedValueSupplier
public static interface CheckedValueSupplier<V> {
public V get () throws Exception;
}
CheckedValue
public class CheckedValue<V> {
private final V v;
private final Optional<Exception> opt;
public Value (V v) {
this.v = v;
}
public Value (Exception e) {
this.opt = Optional.of(e);
}
public V get () throws Exception {
if (opt.isPresent()) {
throw opt.get();
}
return v;
}
public Optional<Exception> getException () {
return opt;
}
public static <T> CheckedValue<T> returns (T t) {
return new CheckedValue<T>(t);
}
public static <T> CheckedValue<T> rethrows (Exception e) {
return new CheckedValue<T>(e);
}
public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
return Result.rethrows(e);
}
}
public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
การใช้
// Don't use this pattern with FileReader, it's meant to be an
// example. FileReader is a Closeable resource and as such should
// be managed in a try-with-resources block or in another safe
// manner that will make sure it is closed properly.
// This will not compile as the FileReader constructor throws
// an IOException.
Function<String, FileReader> sToFr =
(fn) -> new FileReader(Paths.get(fn).toFile());
// Alternative, this will compile.
Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
return CheckedValue.from (
() -> new FileReader(Paths.get("/home/" + f).toFile()));
};
// Single record usage
// The call to get() will propagate the checked exception if it exists.
FileReader readMe = pToFr.apply("/home/README").get();
// List of records usage
List<String> paths = ...; //a list of paths to files
Collection<CheckedValue<FileReader>> frs =
paths.stream().map(pToFr).collect(Collectors.toList());
// Find out if creation of a file reader failed.
boolean anyErrors = frs.stream()
.filter(f -> f.getException().isPresent())
.findAny().isPresent();
เกิดอะไรขึ้น?
อินเทอร์เฟซที่ใช้งานได้เดียวที่สร้างข้อยกเว้นที่เลือกจะถูกสร้างขึ้น ( CheckedValueSupplier
) นี่จะเป็นอินเทอร์เฟซที่ใช้งานได้เพียงตัวเดียวซึ่งอนุญาตให้มีข้อยกเว้นที่ตรวจสอบได้ ส่วนต่อประสานการทำงานอื่น ๆ ทั้งหมดจะใช้ประโยชน์จากCheckedValueSupplier
ตัดรหัสใด ๆ ที่ส่งข้อยกเว้นที่ตรวจสอบ
CheckedValue
ชั้นจะถือผลมาจากการดำเนินการตรรกะใด ๆ ที่พ่นยกเว้นการตรวจสอบ สิ่งนี้จะป้องกันการแพร่กระจายของข้อยกเว้นที่ตรวจสอบจนกว่าจะถึงจุดที่โค้ดพยายามเข้าถึงค่าที่อินสแตนซ์ของCheckedValue
มี
ปัญหาเกี่ยวกับวิธีการนี้
- ตอนนี้เรากำลังขว้าง "ยกเว้น" อย่างมีประสิทธิภาพซ่อนประเภทเฉพาะที่ถูกโยนไป
- เราไม่ทราบว่ามีข้อยกเว้นเกิดขึ้นจนกว่า
CheckedValue#get()
จะมีการเรียก
ผู้บริโภคและคณะ
อินเตอร์เฟซที่ใช้งานได้บางอย่าง (Consumer
ตัวอย่าง) ต้องจัดการในลักษณะที่แตกต่างกันเนื่องจากไม่มีค่าตอบแทน
ฟังก์ชั่นแทนผู้บริโภค
วิธีหนึ่งคือการใช้ฟังก์ชั่นแทนการใช้งานของผู้บริโภคซึ่งใช้เมื่อจัดการกระแสข้อมูล
List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
.map(e -> CheckedValueSupplier.from(
() -> {throwyMethod(e); return e;}))
.filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior
บานปลาย
RuntimeException
หรือคุณสามารถขยายไปยัง มีคำตอบอื่น ๆ ที่ครอบคลุมการยกระดับของข้อยกเว้นที่ตรวจสอบจากภายในConsumer
ที่ปกเพิ่มของการตรวจสอบข้อยกเว้นจากภายในเป็น
อย่ากิน
เพียงหลีกเลี่ยงอินเทอร์เฟซที่ใช้งานได้ทั้งหมดพร้อมกันและใช้รูปลักษณ์ที่ดีสำหรับการวนซ้ำ