Erstellen Sie einen benutzerdefinierten Rückgabetyp, der die überprüfte Ausnahme weitergibt. Dies ist eine Alternative dazu, eine neue Schnittstelle zu erstellen, die die vorhandene funktionale Schnittstelle widerspiegelt, jedoch mit der leichten Modifikation eines "throws exception" für die Methode der funktionalen Schnittstelle.
Definition
CheckedValueSupplier
public static interface CheckedValueSupplier {
public V get () throws Exception;
}
CheckedValue
public class CheckedValue {
private final V v;
private final Optional 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 getException () {
return opt;
}
public static CheckedValue returns (T t) {
return new CheckedValue(t);
}
public static CheckedValue rethrows (Exception e) {
return new CheckedValue(e);
}
public static CheckedValue from (CheckedValueSupplier sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
return Result.rethrows(e);
}
}
public static CheckedValue escalates (CheckedValueSupplier sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Verwendung
// Verwenden Sie dieses Muster nicht mit FileReader, es soll ein Beispiel sein.
// FileReader ist eine Closeable-Ressource und sollte daher in einem try-with-resources-Block oder auf eine andere sichere Weise verwaltet werden, um sicherzustellen, dass sie ordnungsgemäß geschlossen wird.
// Dies wird nicht kompilieren, da der FileReader-Konstruktor eine IOException wirft.
Function sToFr =
(fn) -> new FileReader(Paths.get(fn).toFile());
// Alternativ, dies wird kompilieren.
Function> sToFr = (fn) -> {
return CheckedValue.from (
() -> new FileReader(Paths.get("/home/" + f).toFile()));
};
// Verwendung für einzelne Datensätze
// Der Aufruf von get() gibt die überprüfte Ausnahme weiter, wenn sie vorhanden ist.
FileReader readMe = pToFr.apply("/home/README").get();
// Verwendung für eine Liste von Datensätzen
List paths = ...; //eine Liste von Dateipfaden
Collection> frs =
paths.stream().map(pToFr).collect(Collectors.toList());
// Herausfinden, ob die Erstellung eines FileReader fehlgeschlagen ist.
boolean anyErrors = frs.stream()
.filter(f -> f.getException().isPresent())
.findAny().isPresent();
Was passiert hier?
Es wird eine einzelne funktionale Schnittstelle erstellt, die eine überprüfte Ausnahme wirft (CheckedValueSupplier
). Dies wird die einzige funktionale Schnittstelle sein, die überprüfte Ausnahmen zulässt. Alle anderen funktionalen Schnittstellen werden den CheckedValueSupplier
nutzen, um jeden Code, der eine überprüfte Ausnahme wirft, einzuhüllen.
Die Klasse CheckedValue
wird das Ergebnis der Ausführung jedes Logikteils, das eine überprüfte Ausnahme wirft, halten. Dies verhindert das Weiterleiten einer überprüften Ausnahme, bis der Code versucht, auf den Wert zuzugreifen, den eine Instanz von CheckedValue
enthält.
Die Probleme dieser Herangehensweise.
- Wir werfen jetzt "Exception" und verbergen effektiv den ursprünglich geworfenen spezifischen Typ.
- Wir wissen nicht, dass eine Ausnahme aufgetreten ist, bis
CheckedValue#get()
aufgerufen wird.
Consumer et al
Einige funktionale Schnittstellen (Consumer
zum Beispiel) müssen auf eine andere Weise behandelt werden, da sie keinen Rückgabewert liefern.
Funktion anstelle von Consumer
Ein Ansatz besteht darin, eine Funktion anstelle eines Verbrauchers zu verwenden, was beim Umgang mit Streams zum Tragen kommt.
List lst = Lists.newArrayList();
// wird nicht kompilieren
lst.stream().forEach(e -> throwyMethod(e));
// kompiliert
lst.stream()
.map(e -> CheckedValueSupplier.from(
() -> {throwyMethod(e); return e;}))
.filter(v -> v.getException().isPresent()); //Dieses Beispiel läuft möglicherweise aufgrund des lazy stream-Verhaltens nicht tatsächlich
Eskalation
Alternativ können Sie immer zu einem RuntimeException
eskalieren. Es gibt andere Antworten, die die Eskalation einer überprüften Ausnahme von innerhalb eines Consumers
abdecken.
Nicht verbrauchen.
Vermeiden Sie einfach funktionale Schnittstellen ganz und verwenden Sie eine gute alte for-Schleife.