Die Stream-API von Java ist sehr nützlich, aber die Ausnahmebehandlung ist langwierig.
Es gibt Möglichkeiten, es durch eine Laufzeitausnahme im Lambda zu ersetzen oder Optional zu verwenden, aber ich möchte etwas besser damit umgehen. Ich möchte Entweder als Monade verwenden, weil es eine große Sache ist. Im Moment ist entweder nicht standardmäßig implementiert, aber es scheint, dass dies mithilfe der Bibliothek vavr möglich ist. Es scheint, dass sich der Name der Bibliothek, die früher Javaslang hieß, geändert hat.
Entweder handelt es sich um einen Container, der wie Tupel zwei Elemente in Rechts / Links enthält. Wenn Sie einen Normalwert in Rechts und einen Fehler in Links halten, können Sie die Verarbeitung in einer Kette wie Optional verbinden. Optionale und beide Monaden aus Java8 Stream gelernt.
Daher möchte ich die Verwendung von Entweder mit der Stream-API organisieren.
Was möchten Sie mit dieser Ausnahme tun, wenn Sie mit jedem Element des Streams etwas tun und innerhalb dieses Prozesses eine Ausnahme auftritt? Denken Sie vorerst an die folgenden drei Muster?
Wir werden überlegen, wie diese umgesetzt werden können.
Hier ist ein Beispielcode, um über dieses Problem nachzudenken. Nimmt eine Zeichenfolgenliste als Argument und erstellt eine Ganzzahlliste mit Integer # parseInt dazwischen.
Test.java
public void test() {
List<String> list = Arrays.asList("123", "abc", "45", "def", "789");
List<Integer> result = list.stream() //Mach es Stream
.map(this::parseInt) //Konvertieren Sie einzelne Elemente
.collect(Collectors.toList()); //Zurück zur Liste als Kündigung
}
private Integer parseInt(String data) {
try {
return Integer.parseInt(value);
} catch (Exception ex) {
return null; //Ich drücke den Fehler und mache ihn null
}
}
Dieses Beispiel drückt den Fehler und gibt also null zurück
[123, null, 45, null, 789]
Sie erhalten eine solche Liste.
Halten Sie vavr verfügbar.
build.gradle
dependencies {
...
implementation 'io.vavr:vavr:0.9.3'
}
Beides wird nicht benötigt, da es den Fehler in diesem Fall nicht behandelt.
FilterTest.java
public void testFilterSuccess() {
List<String> data = Arrays.asList("123", "abc", "45", "def", "789");
// List<Integer>
var result = data.stream()
.map(this::parseIntOpt)
.filter(Optional::isPresent).map(Optional::get)
.collect(Collectors.toList());
}
private Optional<Integer> parseIntOpt(String value) {
try {
return Optional.of(Integer.parseInt(value));
} catch (Exception ex) {
return Optional.empty();
}
}
Filtern Sie nur die Elemente mit Werten mit Filter und rufen Sie sie mit der Karte ab, um sie zu beenden.
Das Hauptthema ist von hier.
Ändern Sie zunächst die Elementverarbeitung so, dass sie entweder zurückgibt.
EitherTest.java
private Either<Exception, Integer> parseIntEither(String value) {
try {
return Either.right(Integer.parseInt(value));
} catch (Exception ex) {
return Either.left(ex);
}
}
Das Beispiel wurde geändert, um diese Funktion zu verwenden
EitherTest.java
public void testFirstException() {
List<String> data = Arrays.asList("123", "abc", "45", "def", "789");
// List<Either<Exception, Integer>>
var result = data.stream()
.map(this::parseIntEither)
.collect(Collectors.toList()));
}
Zu diesem Zeitpunkt ist es noch eine Liste von Entweder.
Wenn ein Fehler vorliegt, möchten wir den ersten Fehler extrahieren. Wenn dies normal ist, möchten wir "List
EitherTest.java
// Either<Exception, Seq<Integer>>
var result = Either.sequenceRight(data.stream()
.map(this::parseIntEither)
.collect(Collectors.toList()));
Diese Methode wendet die Liste von Entweder in der Reihenfolge an, und wenn "Entweder.Links ()" vorhanden ist, behält sie ihren Wert bei, und wenn alles "Either.right ()" ist, aggregiert sie "Either.right" (Seq
EitherTest.java
try {
List<Integer> intList = result.getOrElseThrow(ex -> ex).toJavaList();
// handle intList
} catch (Exception ex) {
// handle exception
}
Da Seq
eine in vavr definierte Sequenzklasse ist, wird sie in eine Java-Liste konvertiert. Wenn jedoch kein Problem vorliegt, ist es in Ordnung, die Verarbeitung mit Seq fortzusetzen.
Zusammenfassend lautet der Code also wie folgt.
EitherTest.java
public void testFirstException() {
List<String> data = Arrays.asList("123", "abc", "45", "def", "789");
try {
List<Integer> result = Either.sequenceRight(data.stream()
.map(this::parseIntEither)
.collect(Collectors.toList()))
.getOrElseThrow(ex -> ex).toJavaList();
} catch (Exception ex) {
//Der folgende Fehler wird ausgelöst
// For input string: "abc"
}
}
Ich habe das Gefühl, dass ich es ganz ordentlich schreiben kann.
Es ist das gleiche wie 2. bis zur Mitte. Wenn Sie eine der beiden Sequenzen anstelle der beiden Sequenzen verwenden, werden alle Ausnahmen erfasst und Seq zurückgegeben.
EitherTest.java
// Either<Seq<Exception>, Seq<Integer>>
var result = Either.sequence(data.stream()
.map(this::parseIntEither)
.collect(Collectors.toList()));
Wenn Sie damit die gesammelte Ausnahmeliste mit Falz zusammenstellen, können Sie wie folgt schreiben.
EitherTest.java
public void testAllException() {
List<String> data = Arrays.asList("123", "abc", "45", "def", "789");
try {
List<Integer> result = Either.sequence(data.stream()
.map(this::parseIntEither)
.collect(Collectors.toList()))
.getOrElseThrow(exSeq -> exSeq.fold(new Exception("Multiple Exception"),
(root, value) -> {
root.addSuppressed(value);
return root;
}))
.toJavaList();
} catch (Exception ex) {
// ex.getSuppressed()Zu"abc,"def"Ausnahme mit 2 Ausnahmen wird ausgelöst
}
}
Jetzt können Sie auch die Ausnahmen für jedes Element aggregieren.
Entweder handelt es sich um eine Klasse, die immer entweder den Status Links oder Rechts annimmt. Wenn also eine Ausnahme vorliegt, ist sie isLeft (), andernfalls isRight ().
Wenn es eine Ausnahme gibt, ist es daher nicht möglich, den Normalwert beim Extrahieren der Ausnahme zu erfassen.
Ich bin mir nicht sicher, ob es eine solche Verwendung gibt, aber ich werde auch überlegen, wie man "List <Entweder <Ausnahme, Ganzzahl >>" für Studienzwecke in "Liste
Der Ansatz besteht darin, Stream zu beenden
collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner)
Sammeln Sie zu Tuple2 <List <Ausnahme>, List <Integer >>
von vavr mit.
TupleTest.java
//Tuple2<List<Exception>, List<Integer>>
var result = data.stream()
.map(this::parseIntEither)
.collect(() -> Tuple.of(new ArrayList<Exception>(), new ArrayList<Integer>()),
(accumulator, value) -> {
if (value.isLeft()) {
accumulator._1.add(value.getLeft());
} else {
accumulator._2.add(value.get());
}
},
(left, right) -> {
left._1.addAll(right._1);
left._2.addAll(right._2);
});
Es ist nicht sehr schön, wenn Sie so weit schreiben. .. Wenn Sie es wirklich implementieren möchten, möchten Sie möglicherweise eine separate Klasse erstellen, die die Collector-Schnittstelle implementiert.
** [Ergänzung] **
In den Kommentaren brachte er mir bei, wie man sehr einfach schreibt.
collect(Collectors.collectingAndThen(
Collectors.groupingBy(Either::isLeft),
map -> Tuple.of(map.get(true),map.get(false))
);
Collectors.groupingBy
gruppiert die Stream-Elemente nach dem angegebenen Prozess und gibt eine Map von (Grouping Key-> Element List) zurück.
In diesem Fall erhalten wir "Map <Boolean, Seq <Entweder <Ausnahme, Ganzzahl >>>", indem wir gruppieren, ob entweder :: isLeft wahr oder falsch ist.
Darüber hinaus kann "Collectors.collectingAndThen" den Erfassungsprozess im ersten Argument und den nachfolgenden Prozess im zweiten Argument beschreiben. Das zweite Argument nimmt den Wert aus der Karte und konvertiert ihn in Tupel.
Wenn Sie es wie die anderen Beispiele organisieren, erhalten Sie den folgenden Code.
TupleTest2.java
var result = data.stream()
.map(this::parseIntEither)
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Either::isLeft),
map -> Tuple.of(
Either.sequence(map.get(true)).getLeft().toJavaList(),
Either.sequence(map.get(false)).get().toJavaList())
));
Der Code ist deutlich sauberer. Die Collectors-Methode ist praktisch, nicht wahr? Ich möchte es unten halten.
** [Ergänzung hier] **
Ich hatte das Gefühl, dass die Ausnahmebehandlung von Stream, die ärgerlich war, recht ordentlich geschrieben werden konnte. Ich war eine Weile besorgt darüber, wie ich Entweder ordentlich organisieren sollte, aber natürlich wurde in vavr eine bequeme Methode implementiert. vavr bequem.
Recommended Posts