Einführung in jOOL

1. Übersicht

In diesem Artikel werden wir uns die Freude ansehenBibliothek - ein weiteres Produkt von jOOQ.

2. Maven-Abhängigkeit

Beginnen wir mit dem Hinzufügen einer Maven-Abhängigkeit zu Ihrer pom.xml :

 org.jooq jool 0.9.12  

Die neueste Version finden Sie hier.

3. Funktionale Schnittstellen

In Java 8 sind die funktionalen Schnittstellen sehr begrenzt. Sie akzeptieren die maximale Anzahl von zwei Parametern und verfügen nicht über viele zusätzliche Funktionen.

jOOL behebt dies, indem es eine Reihe neuer Funktionsschnittstellen beweist, die sogar 16 Parameter (von Funktion1 bis Funktion16) akzeptieren können und mit zusätzlichen praktischen Methoden angereichert sind.

Um beispielsweise eine Funktion mit drei Argumenten zu erstellen, können Sie Function3 verwenden:

Function3 lengthSum = (v1, v2, v3) -> v1.length() + v2.length() + v3.length();

In reinem Java müssten Sie es selbst implementieren. Außerdem haben funktionale Schnittstellen von jOOL eine Methode applyPartially () , mit der wir eine Teilanwendung einfach durchführen können:

Function2 addTwoNumbers = (v1, v2) -> v1 + v2; Function1 addToTwo = addTwoNumbers.applyPartially(2); Integer result = addToTwo.apply(5); assertEquals(result, (Integer) 7);

Wenn wir eine Methode vom Typ Function2 haben , können wir sie mithilfe einer toBiFunction () -Methode einfach in eine Standard-Java- BiFunction umwandeln :

BiFunction biFunc = addTwoNumbers.toBiFunction();

Ebenso gibt es eine toFunction () -Methode vom Typ Function1 .

4. Tupel

Ein Tupel ist ein sehr wichtiges Konstrukt in einer funktionalen Programmierwelt. Es ist ein typisierter Container für Werte, bei denen jeder Wert einen anderen Typ haben kann. Tupel werden häufig als Funktionsargumente verwendet .

Sie sind auch sehr nützlich, wenn Sie Transformationen für einen Strom von Ereignissen durchführen. In jOOL haben wir Tupel, die von einem bis zu sechzehn Werten umbrechen können, die von Tuple1 bis zu Tuple16- Typen bereitgestellt werden:

tuple(2, 2)

Und für vier Werte:

tuple(1,2,3,4); 

Betrachten wir ein Beispiel, wenn wir eine Folge von Tupeln haben, die drei Werte enthalten:

Seq
    
      personDetails = Seq.of( tuple("michael", "similar", 49), tuple("jodie", "variable", 43)); Tuple2 tuple = tuple("winter", "summer"); List
     
       result = personDetails .map(t -> t.limit2().concat(tuple)).toList(); assertEquals( result, Arrays.asList(tuple("michael", "similar", "winter", "summer"), tuple("jodie", "variable", "winter", "summer")) );
     
    

Wir können verschiedene Arten von Transformationen für Tupel verwenden. Zuerst rufen wir eine limit2 () -Methode auf, um nur zwei Werte aus Tuple3 zu übernehmen. Dann rufen wir eine concat () -Methode auf, um zwei Tupel zu verketten.

Im Ergebnis erhalten wir Werte vom Typ Tuple4 .

5. Seq

Das Seq- Konstrukt fügt einem Stream übergeordnete Methoden hinzu, während häufig die darunter liegenden Methoden verwendet werden.

5.1. Enthält Operationen

Wir können einige Varianten von Methoden finden, die prüfen, ob Elemente in einer Seq vorhanden sind. Einige dieser Methoden verwenden eine anyMatch () -Methode aus einer Stream- Klasse:

assertTrue(Seq.of(1, 2, 3, 4).contains(2)); assertTrue(Seq.of(1, 2, 3, 4).containsAll(2, 3)); assertTrue(Seq.of(1, 2, 3, 4).containsAny(2, 5)); 

5.2. Join Operations

Wenn wir zwei Streams haben und diese verbinden möchten (ähnlich einer SQL-Join-Operation von zwei Datasets), ist die Verwendung einer Standard- Stream- Klasse keine sehr elegante Methode, um dies zu tun:

Stream left = Stream.of(1, 2, 4); Stream right = Stream.of(1, 2, 3); List rightCollected = right.collect(Collectors.toList()); List collect = left .filter(rightCollected::contains) .collect(Collectors.toList()); assertEquals(collect, Arrays.asList(1, 2));

Wir müssen den richtigen Stream für eine Liste sammeln , um zu verhindern, dass java.lang.IllegalStateException: Der Stream wurde bereits bearbeitet oder geschlossen. Als Nächstes müssen wir eine Nebenwirkungsoperation ausführen, indem wir über eine Filtermethode auf eine rightCollected- Liste zugreifen . Es ist eine fehleranfällige und nicht elegante Art, zwei Datensätze zu verbinden.

Glücklicherweise verfügt Seq über nützliche Methoden, um innere, linke und rechte Verknüpfungen für Datensätze durchzuführen. Diese Methoden verbergen eine Implementierung, die eine elegante API verfügbar macht.

Wir können einen inneren Join mit einer innerJoin () -Methode durchführen:

assertEquals( Seq.of(1, 2, 4).innerJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2)) );

Wir können rechts und links entsprechend verbinden:

assertEquals( Seq.of(1, 2, 4).leftOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(4, null)) ); assertEquals( Seq.of(1, 2, 4).rightOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(null, 3)) );

Es gibt sogar eine crossJoin () -Methode, die es ermöglicht, zwei Datensätze kartesisch zu verbinden:

assertEquals( Seq.of(1, 2).crossJoin(Seq.of("A", "B")).toList(), Arrays.asList(tuple(1, "A"), tuple(1, "B"), tuple(2, "A"), tuple(2, "B")) );

5.3. Manipulating a Seq

Seq has many useful methods for manipulating sequences of elements. Let's look at some of them.

We can use a cycle() method to take repeatedly elements from a source sequence. It will create an infinite stream, so we need to be careful when collecting results to a list thus we need to use a limit() method to transform infinite sequence into finite one:

assertEquals( Seq.of(1, 2, 3).cycle().limit(9).toList(), Arrays.asList(1, 2, 3, 1, 2, 3, 1, 2, 3) );

Let's say that we want to duplicate all elements from one sequence to the second sequence. The duplicate() method does exactly that:

assertEquals( Seq.of(1, 2, 3).duplicate().map((first, second) -> tuple(first.toList(), second.toList())), tuple(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3)) ); 

Returning type of a duplicate() method is a tuple of two sequences.

Let's say that we have a sequence of integers and we want to split that sequence into two sequences using some predicate. We can use a partition() method:

assertEquals( Seq.of(1, 2, 3, 4).partition(i -> i > 2) .map((first, second) -> tuple(first.toList(), second.toList())), tuple(Arrays.asList(3, 4), Arrays.asList(1, 2)) );

5.4. Grouping Elements

Grouping elements by a key using the Stream API is cumbersome and non-intuitive – because we need to use collect() method with a Collectors.groupingBy collector.

Seq hides that code behind a groupBy() method that returns Map so there is no need to use a collect() method explicitly:

Map
    
      expectedAfterGroupBy = new HashMap(); expectedAfterGroupBy.put(1, Arrays.asList(1, 3)); expectedAfterGroupBy.put(0, Arrays.asList(2, 4)); assertEquals( Seq.of(1, 2, 3, 4).groupBy(i -> i % 2), expectedAfterGroupBy );
    

5.5. Skipping Elements

Let's say that we have a sequence of elements and we want to skip elements while a predicate is not matched. When a predicate is satisfied, elements should land in a resulting sequence.

We can use a skipWhile() method for that:

assertEquals( Seq.of(1, 2, 3, 4, 5).skipWhile(i -> i < 3).toList(), Arrays.asList(3, 4, 5) );

We can achieve the same result using a skipUntil() method:

assertEquals( Seq.of(1, 2, 3, 4, 5).skipUntil(i -> i == 3).toList(), Arrays.asList(3, 4, 5) );

5.6. Zipping Sequences

When we're processing sequences of elements, often there is a need to zip them into one sequence.

The zip() API that could be used to zip two sequences into one:

assertEquals( Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c")).toList(), Arrays.asList(tuple(1, "a"), tuple(2, "b"), tuple(3, "c")) );

The resulting sequence contains tuples of two elements.

When we are zipping two sequences, but we want to zip them in a specific way we can pass a BiFunction to a zip() method that defines the way of zipping elements:

assertEquals( Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"), (x, y) -> x + ":" + y).toList(), Arrays.asList("1:a", "2:b", "3:c") );

Sometimes, it is useful to zip sequence with an index of elements in this sequence, via the zipWithIndex() API:

assertEquals( Seq.of("a", "b", "c").zipWithIndex().toList(), Arrays.asList(tuple("a", 0L), tuple("b", 1L), tuple("c", 2L)) );

6. Converting Checked Exceptions to Unchecked

Let's say that we have a method that takes a string and can throw a checked exception:

public Integer methodThatThrowsChecked(String arg) throws Exception { return arg.length(); }

Then we want to map elements of a Stream applying that method to each element. There is no way to handle that exception higher so we need to handle that exception in a map() method:

List collect = Stream.of("a", "b", "c").map(elem -> { try { return methodThatThrowsChecked(elem); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } }).collect(Collectors.toList()); assertEquals( collect, Arrays.asList(1, 1, 1) );

There is not much we can do with that exception because of the design of functional interfaces in Java so in a catch clause, we are converting a checked exception into unchecked one.

Fortunately, in a jOOL there is an Unchecked class that has methods that can convert checked exceptions into unchecked exceptions:

List collect = Stream.of("a", "b", "c") .map(Unchecked.function(elem -> methodThatThrowsChecked(elem))) .collect(Collectors.toList()); assertEquals( collect, Arrays.asList(1, 1, 1) );

Wir verpacken einen Aufruf einer methodThatThrowsChecked () in eine Unchecked.function () -Methode, die die Konvertierung von darunter liegenden Ausnahmen behandelt.

7. Fazit

Dieser Artikel zeigt, wie Sie die jOOL-Bibliothek verwenden, die der Java-Standard- Stream- API nützliche zusätzliche Methoden hinzufügt .

Die Implementierung all dieser Beispiele und Codefragmente finden Sie im GitHub-Projekt - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.