Inleiding tot Atlassian Fuga

1. Inleiding

Fuga is een Java-bibliotheek van Atlassian; het is een verzameling hulpprogramma's die ondersteuning bieden Functioneel programmeren.

In dit artikel zullen we ons concentreren op de belangrijkste API's van Fuga en deze verkennen.

2. Aan de slag met fuga

Om Fuga in onze projecten te kunnen gebruiken, moeten we de volgende afhankelijkheid toevoegen:

 io.atlassian.fuga fuga 4.5.1 

We kunnen de meest recente versie van Fuga vinden op Maven Central.

3. Keuze

Laten we onze reis beginnen door naar het Keuze klasse die het antwoord van Fuga is java.util.Optioneel.

Zoals we aan de naam kunnen raden, Keuze's een container die een potentieel afwezige waarde vertegenwoordigt.

Met andere woorden, een Keuze is ofwel Sommige waarde van een bepaald type of Geen:

Option none = Option.none (); assertFalse (none.isDefined ()); Option some = Option.some ("waarde"); assertTrue (some.isDefined ()); assertEquals ("waarde", some.get ()); Option misschien = Option.option (someInputValue);

3.1. De kaart Operatie

Een van de standaard API's voor functionele programmering is de kaart() methode waarmee een opgegeven functie op onderliggende elementen kan worden toegepast.

De methode past de opgegeven functie toe op het Keuze‘S waarde als het aanwezig is:

Option some = Option.some ("waarde") .map (String :: toUpperCase); assertEquals ("VALUE", some.get ());

3.2. Keuze en een Nul Waarde

Naast het benoemen van verschillen, heeft Atlassian ook een aantal ontwerpkeuzes gemaakt voor Keuze die verschillen van Optioneel; laten we ze nu eens bekijken.

We kunnen niet direct een niet-leeg maken Keuze met een nul waarde:

Option.some (null);

Het bovenstaande werpt een uitzondering op.

We kunnen er echter een krijgen als gevolg van het gebruik van de kaart() operatie:

Option some = Option.some ("waarde") .map (x -> null); assertNull (some.get ());

Dit is niet mogelijk bij gewoon gebruik java.util.Optioneel.

3.3. Optie Is Herhaalbaar

Keuze kan worden behandeld als een verzameling die maximaal één element bevat, dus het is logisch dat het de Herhaalbaar koppel.

Dit verhoogt in hoge mate de interoperabiliteit bij het werken met verzamelingen / streams.

En nu kan bijvoorbeeld worden samengevoegd met een andere collectie:

Option some = Option.some ("waarde"); Iterable strings = Iterables .concat (sommige, Arrays.asList ("a", "b", "c"));

3.4. Omzetten Keuze naar Stroom

Sinds een Keuze is een Herhaalbaar, het kan worden geconverteerd naar een Stroom ook gemakkelijk.

Na het converteren is het Stroom instantie zal precies één element hebben als de optie aanwezig is, of nul anders:

assertEquals (0, Option.none (). toStream (). count ()); assertEquals (1, Option.some ("waarde"). toStream (). count ());

3.5. java.util.Optioneel Interoperabiliteit

Als we een standaard nodig hebben Optioneel implementatie, kunnen we het gemakkelijk verkrijgen met behulp van de naar optioneel () methode:

Optioneel optioneel = Option.none () .toOptional (); assertTrue (Option.fromOptional (optioneel) .isEmpty ());

3.6. De Opties Utility-klasse

Ten slotte biedt Fugue enkele hulpprogramma-methoden om mee te werken Keuzes in de toepasselijke naam Opties klasse.

Het bevat methoden zoals filter Geen voor het verwijderen van lege Opties uit een verzameling, en afvlakken voor beurting een verzameling van Opties in een verzameling ingesloten objecten, leeg filterend Opties.

Bovendien bevat het verschillende varianten van de optillen methode die een Functie in een Functie>:

Functie f = (geheel getal x) -> x> 0? x + 1: nul; Functie lifted = Options.lift (f); assertEquals (2, (long) lifted.apply (Option.some (1)). get ()); assertTrue (lifted.apply (Option.none ()). isEmpty ());

Dit is handig als we een functie willen passeren waarvan we ons niet bewust zijn Keuze naar een methode die gebruikmaakt van Keuze.

Merk op dat, net als de kaart methode, optillen wijst null niet toe aan Geen:

assertEquals (null, lifted.apply (Option.some (0)). get ());

4. Een van beide voor berekeningen met twee mogelijke uitkomsten

Zoals we hebben gezien, is de Keuze class stelt ons in staat om op een functionele manier om te gaan met het ontbreken van een waarde.

Soms moeten we echter meer informatie retourneren dan "geen waarde"; We willen bijvoorbeeld een legitieme waarde of een foutobject retourneren.

De Een van beide class dekt die use case.

Een exemplaar van Een van beide kan een Rechtsaf of een Links maar nooit allebei tegelijk.

Volgens afspraak is rechts het resultaat van een succesvolle berekening, terwijl links het uitzonderlijke geval is.

4.1. Een Een van beide

We kunnen een Een van beide bijvoorbeeld door een van de twee statische fabrieksmethoden aan te roepen.

Wij bellen Rechtsaf als we een Een van beide met de Rechtsaf waarde:

Ofwel rechts = ofwel.rechts ("waarde");

Anders bellen we links:

Ofwel links = ofwel.links (-1);

Hier kan onze berekening ofwel een Draad of een Geheel getal.

4.2. Met behulp van een Een van beide

Als we een Een van beide we kunnen bijvoorbeeld controleren of het links of rechts is en dienovereenkomstig handelen:

if (ofwel.isRight ()) {...}

Interessanter is dat we operaties kunnen koppelen aan een functionele stijl:

ofwel .map (String :: toUpperCase) .getOrNull ();

4.3. Projecties

Het belangrijkste ding dat Ofwel onderscheidt van andere monadische tools zoals Optie, probeer, is het feit dat het vaak onbevooroordeeld is. Simpel gezegd, als we de methode map () aanroepen, Een van beide weet niet of hij moet werken Links of Rechtsaf kant.

Dit is waar projecties van pas komen.

Linker- en rechterprojecties zijn spiegelbeeldige aanzichten van een Een van beide die zich richten op de linker- of rechterwaarde, respectievelijk:

ofwel.left () .map (x -> decodeSQLErrorCode (x));

In het bovenstaande codefragment, if Een van beide is Links, decodeSQLErrorCode () wordt toegepast op het onderliggende element. Als Een van beide is Rechtsaf, het zal niet. Hetzelfde andersom bij het gebruik van de juiste projectie.

4.4. Hulpprogramma's

Net als bij Opties, Fuga biedt een klas vol hulpprogramma's voor Eithers, ook, en het heet zomaar: Eithers.

Het bevat methoden voor het filteren, casten en herhalen van verzamelingen van Een van beides.

5. Afhandeling van uitzonderingen met Proberen

We sluiten onze tour van dit-of-dat-gegevenstypen in Fuga af met een andere variant genaamd Proberen.

Proberen is gelijkaardig aan Een van beide, maar het verschilt doordat het is bedoeld voor het werken met uitzonderingen.

Leuk vinden Keuze en in tegenstelling tot Een van beide, Proberen wordt geparametriseerd over een enkel type, omdat het "andere" type is vastgesteld op Uitzondering (terwijl voor Keuze het is impliciet Ongeldig).

Dus een Proberen kan een Succes of een Mislukking:

assertTrue (Try.failure (nieuwe uitzondering ("Fail!")). isFailure ()); assertTrue (Try.successful ("OK"). isSuccess ());

5.1. Instantiëren van een Proberen

Vaak maken we geen Proberen expliciet als een succes of een mislukking; in plaats daarvan maken we er een op basis van een methodeaanroep.

Gecontroleerd. Van roept een bepaalde functie aan en retourneert een Proberen het inkapselen van de geretourneerde waarde of een gegenereerde uitzondering:

assertTrue (Checked.of (() -> "ok"). isSuccess ()); assertTrue (Checked.of (() -> {throw new Exception ("ko");}). isFailure ());

Een andere methode, Gecontroleerd. Lift, heeft een potentieel werpfunctie en liften het naar een functie die een Proberen:

Checked.Function throwException = (String x) -> {throw new Exception (x); }; assertTrue (Checked.lift (throwException) .apply ("ko"). isFailure ());

5.2. Werken met Proberen

Zodra we een Proberen, zijn de drie meest voorkomende dingen die we er uiteindelijk mee zouden willen doen:

  1. het extraheren van zijn waarde
  2. een operatie koppelen aan de succesvolle waarde
  3. de uitzondering afhandelen met een functie

Trouwens, uiteraard, het weggooien van de Proberen of het doorgeven aan andere methoden, de bovenstaande drie zijn niet de enige opties die we hebben, maar alle andere ingebouwde methoden zijn slechts een gemak boven deze drie.

5.3. De succesvolle waarde extraheren

Om de waarde te extraheren, gebruiken we de getOrElse methode:

assertEquals (42, failedTry.getOrElse (() -> 42));

Het retourneert de succesvolle waarde, indien aanwezig, of een andere berekende waarde.

Er is geen getOrThrow of vergelijkbaar, maar sindsdien getOrElse geen enkele uitzondering opvangt, kunnen we het gemakkelijk schrijven:

someTry.getOrElse (() -> {throw new NoSuchElementException ("Nothing to get");});

5.4. Gesprekken koppelen na succes

In een functionele stijl kunnen we een functie toepassen op de succeswaarde (indien aanwezig) zonder deze eerst expliciet te extraheren.

Dit is het typische kaart methode die we vinden in Keuze, Een van beide en de meeste andere containers en verzamelingen:

Probeer a Try = Try.successful (42) .map (x -> x + 1);

Het retourneert een Proberen zodat we verdere operaties kunnen ketenen.

Natuurlijk hebben we ook de flatMap verscheidenheid:

Try.successful (42) .flatMap (x -> Try.successful (x + 1));

5.5. Herstellen van uitzonderingen

We hebben analoge toewijzingsbewerkingen die werken, met uitzondering van een Proberen (indien aanwezig), in plaats van de succesvolle waarde ervan.

Die methoden verschillen echter doordat hun betekenis is om te herstellen van de uitzondering, d.w.z. om een ​​succesvol Proberen in het standaardgeval.

Zo kunnen we een nieuwe waarde produceren met herstellen:

Probeer herstel = Probeer .failure (nieuwe uitzondering ("boo!")) .Recover ((uitzondering e) -> e.getMessage () + "hersteld."); assertTrue (Recover.isSuccess ()); assertEquals ("boo! hersteld.", Recover.getOrElse (() -> null));

Zoals we kunnen zien, neemt de herstelfunctie de uitzondering als enige argument.

Als de herstelfunctie zichzelf werpt, is het resultaat een andere mislukt Proberen:

Try failure = Try.failure (new Exception ("boo!")). Recover (x -> {throw new RuntimeException (x);}); assertTrue (failure.isFailure ());

De analoog aan flatMap wordt genoemd herstellen met:

Try Recover = Probeer .failure (nieuwe uitzondering ("boo!")) .RecoverWith ((Uitzondering e) -> Try.successful ("opnieuw hersteld!")); assertTrue (Recover.isSuccess ()); assertEquals ("opnieuw hersteld!", Recover.getOrElse (() -> null));

6. Andere hulpprogramma's

Laten we nu even kijken naar enkele van de andere hulpprogramma's in Fuga, voordat we het afronden.

6.1. Paren

EEN Paar is een heel eenvoudige en veelzijdige datastructuur, gemaakt van twee even belangrijke componenten, die Fuga noemt links en Rechtsaf:

Paar paar = Paar.paar (1, "a"); assertEquals (1, (int) pair.left ()); assertEquals ("a", pair.right ());

Fuga biedt niet veel ingebouwde methoden op Paars, naast het in kaart brengen en het applicatieve functorpatroon.

Echter, Paars worden in de hele bibliotheek gebruikt en zijn direct beschikbaar voor gebruikersprogramma's.

De implementatie van Lisp door de volgende arme persoon is slechts een paar toetsaanslagen verwijderd!

6.2. Eenheid

Eenheid is een opsomming met een enkele waarde die bedoeld is om "geen waarde" weer te geven.

Het is een vervanging voor het ongeldige retourtype en Ongeldig klasse, dat maakt een einde aan nul:

Unit doSomething () {System.out.println ("Hallo! Bijwerking"); retour Eenheid (); }

Het is echter nogal verrassend Keuze begrijpt het niet Eenheid, het behandelen als een waarde in plaats van niets.

6.3. Statische hulpprogramma's

We hebben een paar klassen boordevol statische hulpprogramma-methoden die we niet hoeven te schrijven en te testen.

De Functies class biedt methoden die functies op verschillende manieren gebruiken en transformeren: compositie, toepassing, currying, deelfuncties met behulp van Keuze, zwakke memo enzovoort.

De Leveranciers class biedt een vergelijkbare, maar beperktere verzameling hulpprogramma's voor Leveranciers, dat wil zeggen functies zonder argumenten.

Iterables en Iteratoren, ten slotte, bevatten een groot aantal statische methoden voor het manipuleren van deze twee veelgebruikte standaard Java-interfaces.

7. Conclusie

In dit artikel hebben we een overzicht gegeven van de Fuga-bibliotheek van Atlassian.

We hebben de algebra-zware klassen nog niet aangeraakt Monoid en Semigroepen omdat ze niet in een generalistisch artikel passen.

U kunt er echter over en meer lezen in de Fuga javadocs en de broncode.

We hebben ook geen van de optionele modules genoemd, die bijvoorbeeld integraties bieden met Guava en Scala.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in het GitHub-project - dit is een Maven-project, dus het zou gemakkelijk moeten kunnen worden geïmporteerd en uitgevoerd zoals het is.