Introductie tot jOOL

1. Overzicht

In dit artikel zullen we kijken naar de jOOLbibliotheek - een ander product van jOOQ.

2. Maven Afhankelijkheid

Laten we beginnen met het toevoegen van een Maven-afhankelijkheid aan uw pom.xml:

 org.jooq jool 0.9.12 

De laatste versie vind je hier.

3. Functionele interfaces

In Java 8 zijn functionele interfaces vrij beperkt. Ze accepteren het maximale aantal van twee parameters en hebben niet veel extra functies.

jOOL lost dat op door een set nieuwe functionele interfaces te bewijzen die zelfs 16 parameters kunnen accepteren (van Functie 1 tot Functie16) en zijn verrijkt met extra handige methoden.

Om bijvoorbeeld een functie te maken waaraan drie argumenten moeten doorgegeven worden, kunnen we Functie3:

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

In pure Java zou u het zelf moeten implementeren. Daarnaast hebben functionele interfaces van jOOL een methode gedeeltelijk toepassen () waarmee we eenvoudig een gedeeltelijke applicatie kunnen uitvoeren:

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

Als we een methode hebben die van een Functie 2 type, kunnen we het gemakkelijk omzetten naar een standaard Java BiFunction door een toBiFunction () methode:

BiFunction biFunc = addTwoNumbers.toBiFunction ();

Evenzo is er een functioneren() methode in Functie 1 type.

4. Tupels

Een tuple is een zeer belangrijk construct in een functionele programmeerwereld. Het is een getypte container voor waarden waarbij elke waarde een ander type kan hebben. Tupels worden vaak gebruikt als functieargumenten.

Ze zijn ook erg handig bij het uitvoeren van transformaties in een reeks evenementen. In jOOL hebben we tupels die een tot zestien waarden kunnen bevatten, geleverd door Tuple1 tot Tuple16 types:

tupel (2, 2)

En voor vier waarden:

tupel (1,2,3,4); 

Laten we een voorbeeld bekijken wanneer we een reeks tupels hebben die 3 waarden droegen:

Seq personDetails = Seq.of (tuple ("michael", "gelijkaardig", 49), tuple ("jodie", "variable", 43)); Tuple2 tuple = tuple ("winter", "zomer"); Lijst resultaat = personDetails .map (t -> t.limit2 (). concat (tuple)). toList (); assertEquals (resultaat, Arrays.asList (tuple ("michael", "gelijkaardig", "winter", "zomer"), tuple ("jodie", "variable", "winter", "zomer")));

We kunnen verschillende soorten transformaties op tupels gebruiken. Ten eerste noemen we een limit2 () methode om slechts twee waarden uit te halen Tuple3. Dan bellen we een concat () methode om twee tupels samen te voegen.

In het resultaat krijgen we waarden die van a zijn Tuple4 type.

5. Seq

De Seq construct voegt methoden van een hoger niveau toe aan een Stroom terwijl vaak de onderstaande methoden worden gebruikt.

5.1. Bevat Operations

We kunnen een aantal varianten vinden van methoden die controleren op de aanwezigheid van elementen in een Seq. Sommige van die methoden gebruiken een anyMatch () methode van een Stroom klasse:

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

5.2. Doe mee met Operations

Als we twee streams hebben en we willen ze samenvoegen (vergelijkbaar met een SQL-join-bewerking van twee datasets), met behulp van een standaard Stroom les is geen erg elegante manier om dit te doen:

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

We moeten verzamelen Rechtsaf stream naar een lijst om te voorkomen java.lang.IllegalStateException: stream is al bewerkt of gesloten. Vervolgens moeten we een bijwerking uitvoeren door een rightCollected lijst van een filter methode. Het is foutgevoelig en geen elegante manier om twee gegevenssets samen te voegen.

Gelukkig,Seq heeft handige methoden om innerlijke, linker en rechter joins op gegevenssets te maken. Die methoden verbergen een implementatie ervan die een elegante API blootlegt.

We kunnen een innerlijke join maken door een innerJoin () methode:

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

We kunnen de juiste en linker joins dienovereenkomstig doen:

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)));

Er is zelfs een crossJoin () methode die het mogelijk maakt om een ​​cartesische koppeling te maken van twee datasets:

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

5.3. Manipuleren van een Seq

Seq heeft veel bruikbare methoden voor het manipuleren van reeksen elementen. Laten we er een paar bekijken.

We kunnen een fiets() methode om herhaaldelijk elementen uit een bronreeks te halen. Het zal een oneindige stroom creëren, dus we moeten voorzichtig zijn bij het verzamelen van resultaten voor een lijst, dus we moeten een limiet() methode om een ​​oneindige reeks om te zetten in een eindige reeks:

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

Laten we zeggen dat we alle elementen van de ene reeks naar de tweede reeks willen dupliceren. De duplicaat() methode doet precies dat:

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))); 

Retournerende type a duplicaat() methode is een tupel van twee reeksen.

Laten we zeggen dat we een reeks gehele getallen hebben en we willen die reeks opsplitsen in twee reeksen met behulp van een predikaat. We kunnen een partitie () methode:

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. Elementen groeperen

Elementen groeperen met een sleutel met behulp van de Stroom API is omslachtig en niet-intuïtief - omdat we verzamelen() methode met een Collectors.groupingBy verzamelaar.

Seq verbergt die code achter een groupBy () methode die terugkeert Kaart dus het is niet nodig om een verzamelen() methode expliciet:

Kaart verwachteAfterGroupBy = nieuwe HashMap (); verwachteAfterGroupBy.put (1, Arrays.asList (1, 3)); verwachteAfterGroupBy.put (0, Arrays.asList (2, 4)); assertEquals (Seq.of (1, 2, 3, 4) .groupBy (i -> i% 2), verwachtAfterGroupBy);

5.5. Elementen overslaan

Laten we zeggen dat we een reeks elementen hebben en we willen elementen overslaan terwijl een predikaat niet overeenkomt. Wanneer aan een predikaat is voldaan, moeten elementen in een resulterende reeks landen.

We kunnen een skipWhile () methode daarvoor:

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

We kunnen hetzelfde resultaat bereiken met een skipUntil () methode:

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

5.6. Zipping-reeksen

Wanneer we reeksen elementen verwerken, is het vaak nodig om ze in één reeks te zippen.

De zip () API die kan worden gebruikt om twee reeksen in één te zippen:

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

De resulterende reeks bevat tupels van twee elementen.

Als we twee reeksen zippen, maar we ze op een specifieke manier willen zippen, kunnen we een BiFunction naar een zip () methode die de manier van zippen van elementen definieert:

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"));

Soms is het handig om een ​​reeks te zippen met een index van elementen in deze reeks, via de zipWithIndex () API:

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

6. Geconverteerde uitzonderingen converteren naar niet-aangevinkt

Laten we zeggen dat we een methode hebben waaraan een string moet doorgegeven worden en die een gecontroleerde uitzondering kan genereren:

public Integer methodThatThrowsChecked (String arg) genereert Uitzondering {return arg.length (); }

Vervolgens willen we elementen van een Stroom die methode toepassen op elk element. Er is geen manier om die uitzondering hoger af te handelen, dus we moeten die uitzondering afhandelen in een kaart() methode:

List collect = Stream.of ("a", "b", "c"). Map (elem -> {probeer {return methodThatThrowsChecked (elem);} catch (uitzondering e) {e.printStackTrace (); gooi nieuwe RuntimeException (e);}}) verzamel (Collectors.toList ()); assertEquals (collect, Arrays.asList (1, 1, 1));

Er is niet veel dat we kunnen doen met die uitzondering vanwege het ontwerp van functionele interfaces in Java, dus in een catch-clausule converteren we een aangevinkte uitzondering naar een niet-aangevinkte uitzondering.

Gelukkig is er in een jOOL een Niet aangevinkt klasse die methoden heeft die aangevinkte uitzonderingen kunnen omzetten in ongecontroleerde uitzonderingen:

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

We sluiten een oproep af naar een methodThatThrowsChecked () in een Niet aangevinkt. Functie () methode die het converteren van onderliggende uitzonderingen afhandelt.

7. Conclusie

Dit artikel laat zien hoe u de jOOL-bibliotheek gebruikt die nuttige aanvullende methoden toevoegt aan de Java-standaard Stroom API.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in het GitHub-project - dit is een Maven-project, dus het moet gemakkelijk te importeren en uit te voeren zijn zoals het is.