Guide To Java 8 Optioneel

1. Overzicht

In deze tutorial laten we de Optioneel klasse die werd geïntroduceerd in Java 8.

Het doel van de klasse is om een ​​oplossing op typeniveau te bieden voor het weergeven van optionele waarden in plaats van nul referenties.

Om een ​​beter begrip te krijgen van waarom we ons zorgen moeten maken over het Optioneel class, bekijk dan het officiële Oracle-artikel.

2. Creëren Optioneel Voorwerpen

Er zijn verschillende manieren om te creëren Optioneel voorwerpen.

Om een ​​leeg Optioneel object, we moeten gewoon zijn leeg() statische methode:

@Test openbare leegte whenCreatesEmptyOptional_thenCorrect () {Optioneel leeg = Optioneel.empty (); assertFalse (empty.isPresent ()); }

Merk op dat we de is aanwezig() methode om te controleren of er een waarde in de Optioneel voorwerp. Een waarde is alleen aanwezig als we hebben gecreëerd Optioneel met een niet-nul waarde. We zullen kijken naar de is aanwezig() methode in de volgende sectie.

We kunnen ook een Optioneel object met de statische methode van():

@Test openbare ongeldig gegevenNonNull_whenCreatesNonNullable_thenCorrect () {String name = "baeldung"; Optioneel opt = Optioneel.of (naam); assertTrue (opt.isPresent ()); }

Het argument is echter doorgegeven aan de van() methode kan niet zijn nul. Anders krijgen we een NullPointerException:

@Test (verwacht = NullPointerException.class) openbare ongeldige gegevenNull_whenThrowsErrorOnCreate_thenCorrect () {Stringnaam = null; Optioneel. Van (naam); }

Maar voor het geval we wat verwachten nul waarden, kunnen we de ofNullable () methode:

@Test openbare ongeldig gegevenNonNull_whenCreatesNullable_thenCorrect () {String name = "baeldung"; Optioneel opt = Optioneel.ofNullable (naam); assertTrue (opt.isPresent ()); }

Door dit te doen, als we een nul verwijzing, het genereert geen uitzondering maar retourneert eerder een leeg Optioneel voorwerp:

@ Test openbare ongeldig gegevenNull_whenCreatesNullable_thenCorrect () {String naam = null; Optioneel opt = Optioneel.ofNullable (naam); assertFalse (opt.isPresent ()); }

3. Aanwezigheid van waarde controleren: is aanwezig() en is leeg()

Als we een Optioneel object geretourneerd door een methode of door ons gemaakt, kunnen we controleren of er een waarde in zit of niet met de is aanwezig() methode:

@Test openbare ongeldig gegevenOptional_whenIsPresentWorks_thenCorrect () {Optioneel opt = Optioneel.of ("Baeldung"); assertTrue (opt.isPresent ()); opt = Optioneel.ofNullable (null); assertFalse (opt.isPresent ()); }

Deze methode keert terug waar als de ingepakte waarde dat niet is nul.

Vanaf Java 11 kunnen we ook het tegenovergestelde doen met de is leeg methode:

@Test openbare ongeldig gegevenAnEmptyOptional_thenIsEmptyBehavesAsExpected () {Optioneel opt = Optioneel.of ("Baeldung"); assertFalse (opt.isEmpty ()); opt = Optioneel.ofNullable (null); assertTrue (opt.isEmpty ()); }

4. Voorwaardelijke actie met indien aanwezig()

De indien aanwezig() methode stelt ons in staat om wat code uit te voeren op de ingepakte waarde als deze niet blijkt te zijnnul. Voordat Optioneel, zouden we doen:

if (name! = null) {System.out.println (name.length ()); }

Deze code controleert of de naamvariabele nul of niet voordat je doorgaat om er wat code op uit te voeren. Deze aanpak duurt lang, en dat is niet het enige probleem - het is ook vatbaar voor fouten.

Inderdaad, wat garandeert dat na het afdrukken van die variabele, we deze niet opnieuw en dan zullen gebruiken vergeet de nulcontrole uit te voeren?

Dit kan resulteren in een NullPointerException tijdens runtime als een null-waarde zijn weg vindt naar die code. Wanneer een programma mislukt vanwege invoerproblemen, is dit vaak een gevolg van slechte programmeerpraktijken.

Optioneel laat ons expliciet omgaan met nulbare waarden als een manier om goede programmeerpraktijken af ​​te dwingen.

Laten we nu eens kijken hoe de bovenstaande code kan worden geherstructureerd in Java 8.

In een typische functionele programmeerstijl kunnen we een actie uitvoeren op een object dat daadwerkelijk aanwezig is:

@Test openbare ongeldig gegevenOptional_whenIfPresentWorks_thenCorrect () {Optioneel opt = Optioneel.of ("baeldung"); opt.ifPresent (naam -> System.out.println (name.length ())); }

In het bovenstaande voorbeeld gebruiken we slechts twee regels code om de vijf te vervangen die in het eerste voorbeeld werkten: één regel om het object in een Optioneel object en de volgende om impliciete validatie uit te voeren en de code uit te voeren.

5. Standaardwaarde met of anders()

De of anders() methode wordt gebruikt om de waarde op te halen die in een Optioneel voorbeeld. Er is één parameter voor nodig, die fungeert als een standaardwaarde. De of anders() methode retourneert de ingepakte waarde als deze aanwezig is, en het argument anders:

@Test openbare leegte whenOrElseWorks_thenCorrect () {String nullName = null; String name = Optioneel.ofNullable (nullName) .orElse ("john"); assertEquals ("john", naam); }

6. Standaardwaarde met orElseGet ()

De orElseGet () methode is vergelijkbaar met of anders(). In plaats van een waarde te nemen om te retourneren als de Optioneel waarde is niet aanwezig, er is een functionele interface van de leverancier voor nodig, die wordt aangeroepen en de waarde van de aanroep retourneert:

@Test openbare leegte whenOrElseGetWorks_thenCorrect () {String nullName = null; String name = Optioneel.ofNullable (nullName) .orElseGet (() -> "john"); assertEquals ("john", naam); }

7. Verschil tussen of anders en orElseGet ()

Voor veel programmeurs die nieuw zijn Optioneel of Java 8, het verschil tussen of anders() en orElseGet () is niet duidelijk. Deze twee methoden wekken in feite de indruk dat ze elkaar in functionaliteit overlappen.

Er is echter een subtiel maar zeer belangrijk verschil tussen de twee die de prestaties van onze code drastisch kunnen beïnvloeden als ze niet goed worden begrepen.

Laten we een methode maken met de naam getMyDefault () in de testklasse, die geen argumenten accepteert en een standaardwaarde retourneert:

public String getMyDefault () {System.out.println ("Standaardwaarde ophalen"); retourneer "Standaardwaarde"; }

Laten we twee tests bekijken en hun bijwerkingen bekijken om beide vast te stellen waar of anders() en orElseGet () overlappen en waar ze verschillen:

@Test openbare leegte whenOrElseGetAndOrElseOverlap_thenCorrect () {String text = null; String defaultText = Optioneel.ofNullable (tekst) .orElseGet (dit :: getMyDefault); assertEquals ("Standaardwaarde", defaultText); defaultText = Optioneel.ofNullable (tekst) .orElse (getMyDefault ()); assertEquals ("Standaardwaarde", defaultText); }

In het bovenstaande voorbeeld plaatsen we een null-tekst in een Optioneel object en probeer de ingepakte waarde te krijgen met behulp van elk van de twee benaderingen.

De bijwerking is:

Standaardwaarde ophalen ... Standaardwaarde ophalen ...

De getMyDefault () methode wordt in elk geval aangeroepen. Het gebeurt zo dat als de ingepakte waarde niet aanwezig is, dan beide of anders() en orElseGet () werken op precies dezelfde manier.

Laten we nu nog een test uitvoeren waarbij de waarde aanwezig is, en idealiter zou de standaardwaarde niet eens moeten worden gemaakt:

@Test public void whenOrElseGetAndOrElseDiffer_thenCorrect () {String text = "Tekst aanwezig"; System.out.println ("orElseGet gebruiken:"); String defaultText = Optioneel.ofNullable (tekst) .orElseGet (dit :: getMyDefault); assertEquals ("Tekst aanwezig", defaultText); System.out.println ("orElse gebruiken:"); defaultText = Optioneel.ofNullable (tekst) .orElse (getMyDefault ()); assertEquals ("Tekst aanwezig", defaultText); }

In het bovenstaande voorbeeld verpakken we niet langer een nul waarde, en de rest van de code blijft hetzelfde.

Laten we nu eens kijken naar de bijwerking van het uitvoeren van deze code:

OrElseGet gebruiken: orElse gebruiken: standaardwaarde ophalen ...

Merk op dat bij gebruik orElseGet () om de ingepakte waarde op te halen, de getMyDefault () methode wordt niet eens aangeroepen omdat de ingesloten waarde aanwezig is.

Bij gebruik van of anders(), of de ingepakte waarde nu aanwezig is of niet, het standaardobject wordt gemaakt. Dus in dit geval hebben we zojuist een redundant object gemaakt dat nooit wordt gebruikt.

In dit eenvoudige voorbeeld zijn er geen significante kosten voor het maken van een standaardobject, aangezien de JVM weet hoe hiermee om te gaan. Wanneer een methode zoals getMyDefault () een webservice moet bellen of zelfs een database moet doorzoeken, worden de kosten heel duidelijk.

8. Uitzonderingen met orElseThrow ()

De orElseThrow () methode volgt uit of anders() en orElseGet () en voegt een nieuwe benadering toe voor het omgaan met een afwezige waarde.

In plaats van een standaardwaarde te retourneren wanneer de ingepakte waarde niet aanwezig is, genereert het een uitzondering:

@Test (verwacht = IllegalArgumentException.class) public void whenOrElseThrowWorks_thenCorrect () {String nullName = null; Stringnaam = Optioneel.ofNullable (nullName) .orElseThrow (IllegalArgumentException :: nieuw); }

Methodeverwijzingen in Java 8 zijn hier handig om door te geven in de exception constructor.

Java 10 introduceerde een vereenvoudigde no-arg-versie van orElseThrow () methode. In het geval van een leeg Optioneel het gooit een NoSuchElelementException:

@Test (verwacht = NoSuchElementException.class) public void whenNoArgOrElseThrowWorks_thenCorrect () {String nullName = null; String name = Optioneel.ofNullable (nullName) .orElseThrow (); }

9. Waarde teruggeven met krijgen()

De laatste benadering voor het ophalen van de ingepakte waarde is de krijgen() methode:

@Test openbare ongeldig gegevenOptional_whenGetsValue_thenCorrect () {Optioneel opt = Optioneel.of ("baeldung"); String naam = opt.get (); assertEquals ("baeldung", naam); }

In tegenstelling tot de vorige drie benaderingen, krijgen() kan alleen een waarde retourneren als het ingepakte object dat niet is nul; anders gooit het een dergelijke elementuitzondering niet:

@Test (verwacht = NoSuchElementException.class) openbare ongeldige gegevenOptionalWithNull_whenGetThrowsException_thenCorrect () {Optioneel opt = Optioneel.ofNullable (null); String naam = opt.get (); }

Dit is de grootste fout van de krijgen() methode. Ideaal, Optioneel zou ons moeten helpen om dergelijke onvoorziene uitzonderingen te vermijden. Daarom werkt deze benadering in tegen de doelstellingen van Optioneel en zal waarschijnlijk worden afgeschaft in een toekomstige release.

Het is dus aan te raden om de andere varianten te gebruiken waarmee we ons kunnen voorbereiden op en expliciet kunnen omgaan met het nul geval.

10. Voorwaardelijke terugkeer met filter()

We kunnen een inline-test uitvoeren op onze ingepakte waarde met de filter methode. Het neemt een predikaat als argument en retourneert een Optioneel voorwerp. Als de ingepakte waarde de test doorstaat met het predikaat, dan is de Optioneel wordt geretourneerd zoals het is.

Als het predikaat echter terugkeert false, dan zal het een lege retourneren Optioneel:

@Test public void whenOptionalFilterWorks_thenCorrect () {Integer year = 2016; Optioneel jaar Optioneel = Optioneel.of (jaar); boolean is2016 = yearOptional.filter (y -> y == 2016) .isPresent (); assertTrue (is2016); boolean is2017 = yearOptional.filter (y -> y == 2017) .isPresent (); assertFalse (is2017); }

De filter methode wordt normaal gesproken op deze manier gebruikt om ingepakte waarden te weigeren op basis van een vooraf gedefinieerde regel. We zouden het kunnen gebruiken om een ​​verkeerd e-mailformaat of een wachtwoord dat niet sterk genoeg is, te weigeren.

Laten we naar een ander zinvol voorbeeld kijken. Stel dat we een modem willen kopen, en we geven alleen om de prijs.

We ontvangen push-notificaties over modemprijzen van een bepaalde site en slaan deze op in objecten:

openbare klasse Modem {privé Dubbele prijs; openbare modem (dubbele prijs) {this.price = price; } // standaard getters en setters}

We sturen deze objecten vervolgens naar een code waarvan het enige doel is om te controleren of de modemprijs binnen ons budgetbereik valt.

Laten we nu eens kijken naar de code zonder Optioneel:

openbare boolean priceIsInRange1 (modemmodem) {boolean isInRange = false; if (modem! = null && modem.getPrice ()! = null && (modem.getPrice ()> = 10 && modem.getPrice () <= 15)) {isInRange = true; } terugkeer isInRange; }

Let op hoeveel code we moeten schrijven om dit te bereiken, vooral in de als staat. Het enige deel van de als voorwaarde die kritiek is voor de toepassing is de laatste prijsklassecontrole; de rest van de cheques zijn defensief:

@Test openbare leegte whenFiltersWithoutOptional_thenCorrect () {assertTrue (priceIsInRange1 (nieuwe modem (10.0))); assertFalse (priceIsInRange1 (nieuwe modem (9.9))); assertFalse (priceIsInRange1 (nieuwe modem (null))); assertFalse (priceIsInRange1 (nieuwe modem (15.5))); assertFalse (priceIsInRange1 (null)); }

Afgezien daarvan is het mogelijk om de null-controles gedurende een lange dag te vergeten zonder compileerfouten te krijgen.

Laten we nu eens kijken naar een variant met Optioneel # filter:

openbare booleaanse prijsIsInRange2 (Modem modem2) {return Optioneel.ofNullable (modem2) .map (Modem :: getPrice) .filter (p -> p> = 10) .filter (p -> p <= 15) .isPresent (); }

De kaart call wordt gewoon gebruikt om een ​​waarde naar een andere waarde te transformeren. Houd er rekening mee dat deze bewerking de oorspronkelijke waarde niet wijzigt.

In ons geval krijgen we een prijsobject van de Model klasse. We zullen kijken naar de kaart() methode in detail in de volgende sectie.

Allereerst, als a nul object wordt doorgegeven aan deze methode, verwachten we geen enkel probleem.

Ten tweede is de enige logica die we in het lichaam schrijven, precies wat de naam van de methode beschrijft: controle van de prijsklasse. Optioneel zorgt voor de rest:

@Test openbare leegte whenFiltersWithOptional_thenCorrect () {assertTrue (priceIsInRange2 (nieuwe modem (10.0))); assertFalse (priceIsInRange2 (nieuwe modem (9.9))); assertFalse (priceIsInRange2 (nieuwe modem (null))); assertFalse (priceIsInRange2 (nieuwe modem (15.5))); assertFalse (priceIsInRange2 (null)); }

De vorige benadering belooft het prijsbereik te controleren, maar moet meer doen dan dat om zich te verdedigen tegen de inherente kwetsbaarheid ervan. Daarom kunnen we de filter methode om onnodig te vervangen als uitspraken en verwerpen ongewenste waarden.

11. Waarde transformeren met kaart()

In de vorige sectie hebben we gekeken hoe we een waarde kunnen weigeren of accepteren op basis van een filter.

We kunnen een vergelijkbare syntaxis gebruiken om het Optioneel waarde met de kaart() methode:

@Test openbare ongeldig gegevenOptional_whenMapWorks_thenCorrect () {List companyNames = Arrays.asList ("paypal", "oracle", "", "microsoft", "", "apple"); Optioneel listOptional = Optioneel.of (companyNames); int size = listOptional .map (List :: size) .orElse (0); assertEquals (6, grootte); }

In dit voorbeeld wikkelen we een lijst met strings in een Optioneel object en gebruik zijn kaart methode om een ​​actie uit te voeren op de ingesloten lijst. De actie die we uitvoeren is om de grootte van de lijst op te halen.

De kaart methode retourneert het resultaat van de berekening die erin is verpakt Optioneel. We moeten dan een geschikte methode aanroepen op de geretourneerde Optioneel om de waarde ervan op te halen.

Merk op dat de filter methode voert eenvoudig een controle uit op de waarde en retourneert een boolean. De kaart methode neemt echter de bestaande waarde, voert een berekening uit met deze waarde en retourneert het resultaat van de berekening verpakt in een Optioneel voorwerp:

@Test openbare ongeldig gegevenOptional_whenMapWorks_thenCorrect2 () {String name = "baeldung"; Optioneel nameOptional = Optioneel.of (naam); int len ​​= naam Optioneel .map (String :: lengte) .orElse (0); assertEquals (8, len); }

We kunnen ketenen kaart en filter samen om iets krachtigers te doen.

Laten we aannemen dat we de juistheid willen controleren van een wachtwoord dat door een gebruiker is ingevoerd. We kunnen het wachtwoord opschonen met een kaart transformatie en controleer de juistheid met behulp van een filter:

@Test openbare ongeldig gegevenOptional_whenMapWorksWithFilter_thenCorrect () {String wachtwoord = "wachtwoord"; Optioneel passOpt = Optioneel.of (wachtwoord); boolean correctPassword = passOpt.filter (pass -> pass.equals ("wachtwoord")). isPresent (); assertFalse (correctPassword); correctPassword = passOpt .map (String :: trim) .filter (pass -> pass.equals ("wachtwoord")) .isPresent (); assertTrue (correctPassword); }

Zoals we kunnen zien, wordt deze, zonder eerst de invoer op te schonen, uitgefilterd - toch kunnen gebruikers als vanzelfsprekend aannemen dat voorloop- en volgspaties allemaal invoer vormen. Dus transformeren we een vuil wachtwoord in een schoon wachtwoord met een kaart voordat u de verkeerde uitfiltert.

12. Waarde transformeren met flatMap ()

Net als de kaart() methode, we hebben ook de flatMap () methode als alternatief voor het omzetten van waarden. Het verschil is dat kaart transformeert waarden alleen wanneer ze worden uitgepakt terwijl flatMap neemt een ingepakte waarde en pakt deze uit voordat deze wordt getransformeerd.

Eerder hebben we simple Draad en Geheel getal objecten om te verpakken in een Optioneel voorbeeld. Vaak ontvangen we deze objecten echter van een accessor van een complex object.

Om een ​​duidelijker beeld te krijgen van het verschil, laten we eens kijken naar een Persoon object dat de gegevens van een persoon, zoals naam, leeftijd en wachtwoord, opneemt:

openbare klasse Persoon {privé Stringnaam; privé int leeftijd; privé String-wachtwoord; openbaar Optioneel getName () {retourneer Optioneel.ofNullable (naam); } public Optioneel getAge () {return Optioneel.ofNullable (leeftijd); } openbaar Optioneel getPassword () {retourneer Optioneel.ofNullable (wachtwoord); } // normale constructeurs en setters}

Normaal gesproken zouden we zo'n object maken en het in een Optioneel object net zoals we deden met String.

Als alternatief kan het naar ons worden geretourneerd door een andere methodeaanroep:

Persoon persoon = nieuwe persoon ("john", 26); Optioneel personOptional = Optioneel.of (persoon);

Merk nu op dat wanneer we een Persoon object, bevat het geneste Optioneel gevallen:

@Test openbare ongeldig gegevenOptional_whenFlatMapWorks_thenCorrect2 () {Persoon persoon = nieuwe persoon ("john", 26); Optioneel personOptional = Optioneel.of (persoon); Optioneel nameOptionalWrapper = personOptional.map (Persoon :: getName); Optioneel nameOptional = nameOptionalWrapper.orElseThrow (IllegalArgumentException :: nieuw); String name1 = nameOptional.orElse (""); assertEquals ("john", name1); String name = personOptional .flatMap (Persoon :: getName) .orElse (""); assertEquals ("john", naam); }

Hier proberen we het name-attribuut van het Persoon bezwaar maken om een ​​bewering uit te voeren.

Let op hoe we dit hiermee bereiken kaart() methode in de derde verklaring, en merk dan op hoe we hetzelfde doen met flatMap () methode achteraf.

De Persoon :: getName method reference is vergelijkbaar met de String :: trim oproep die we in de vorige sectie hadden voor het opschonen van een wachtwoord.

Het enige verschil is dat getName () geeft een Optioneel in plaats van een tekenreeks, zoals de bijsnijden () operatie. Dit, in combinatie met het feit dat a kaart transformatie verpakt het resultaat in een Optioneel object, leidt naar een genest Optioneel.

Tijdens het gebruik kaart() methode, daarom moeten we een extra aanroep toevoegen om de waarde op te halen voordat we de getransformeerde waarde gebruiken. Op deze manier kan de Optioneel wrapper wordt verwijderd. Deze bewerking wordt impliciet uitgevoerd tijdens het gebruik van flatMap.

13. Chaining Optioneels in Java 8

Soms moeten we misschien de eerste niet-lege halen Optioneel object van een aantal Optioneels. In dergelijke gevallen zou het erg handig zijn om een ​​methode te gebruiken zoals orElseOptioneel (). Helaas wordt een dergelijke bewerking niet rechtstreeks ondersteund in Java 8.

Laten we eerst een paar methoden introduceren die we in deze sectie zullen gebruiken:

private Optioneel getEmpty () {return Optioneel.empty (); } private Optioneel getHello () {return Optional.of ("hallo"); } private Optioneel getBye () {return Optioneel.of ("bye"); } private Optioneel createOptional (String input) {if (input == null || "" .equals (input) || "empty" .equals (input)) {return Optional.empty (); } return Optioneel.of (input); }

Om er meerdere te ketenen Optioneel objecten en krijg de eerste niet-lege in Java 8, we kunnen de Stroom API:

@Test openbare leegte gegevenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturned () {Optioneel gevonden = Stream.of (getEmpty (), getHello (), getBye ()) .filter (Optioneel :: isPresent) .map (Optioneel :: get) .findFirst (); assertEquals (getHello (), gevonden); }

De keerzijde van deze aanpak is dat al onze krijgen methoden worden altijd uitgevoerd, ongeacht waar ze niet leeg zijn Optioneel verschijnt in de Stroom.

Als we de methoden die zijn doorgegeven aan lui willen evalueren Stroom van(), moeten we de methodeverwijzing en de Leverancier koppel:

@Test openbare leegte gegevenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturnedAndRestNotEvaluated () {Optioneel gevonden = Stream.<>> of (this :: getEmpty, this :: getHello, this :: getBye) .map (Leverancier :: get) .filter (Optioneel :: isPresent) .map (Optioneel :: get) .findFirst (); assertEquals (getHello (), gevonden); }

In het geval dat we methoden moeten gebruiken die argumenten accepteren, moeten we onze toevlucht nemen tot lambda-expressies:

@Test openbare leegte gegevenTwoOptionalsReturnedByOneArgMethod_whenChaining_thenFirstNonEmptyIsReturned () {Optioneel gevonden = Stream.<>> of (() -> createOptional ("empty"), () -> createOptional ("hallo")) .map (Leverancier :: get) .filter (Optioneel :: isPresent) .map (Optioneel :: get). findFirst (); assertEquals (createOptional ("hallo"), gevonden); }

Vaak willen we een standaardwaarde retourneren voor het geval alles is gekoppeld Optioneels zijn leeg. We kunnen dit doen door een oproep toe te voegen aan of anders() of orElseGet ():

@Test openbare ongeldig gegevenTwoEmptyOptionals_whenChaining_thenDefaultIsReturned () {String gevonden = Stream.<>> van (() -> createOptional ("leeg"), () -> createOptional ("leeg")) .map (Leverancier :: get) .filter (Optioneel :: isPresent) .map (Optioneel :: get). findFirst () .orElseGet (() -> "standaard"); assertEquals ("default", gevonden); }

14. JDK 9 Optioneel API

De release van Java 9 heeft nog meer nieuwe methoden toegevoegd aan het Optioneel API:

  • of() methode om een ​​leverancier te bieden die een alternatief creëert Optioneel
  • ifPresentOrElse () methode waarmee een actie kan worden uitgevoerd als het Optioneel aanwezig is of een andere actie als dat niet het geval is
  • stroom() methode voor het converteren van een Optioneel naar een Stroom

Hier is het volledige artikel om verder te lezen.

15. Misbruik van Optioneels

Laten we tot slot eens kijken naar een verleidelijke, hoe gevaarlijke manier om het te gebruiken Optioneels: passeren van een Optioneel parameter naar een methode.

Stel je voor dat we een lijst hebben met Persoon en we willen een methode om door die lijst te zoeken naar mensen met een bepaalde naam. We willen ook dat die methode overeenkomt met inzendingen met ten minste een bepaalde leeftijd, als deze is gespecificeerd.

Omdat deze parameter optioneel is, komen we met deze methode:

openbare statische lijstzoekopdracht (lijst personen, tekenreeksnaam, optionele leeftijd) {// Null-controles voor personen en naam retourneren mensen.stream () .filter (p -> p.getNaam (). is gelijk aan (naam)) .filter (p -> p.getAge (). get ()> = age.orElse (0)) .collect (Collectors.toList ()); }

Vervolgens geven we onze methode vrij, en een andere ontwikkelaar probeert deze te gebruiken:

someObject.search (mensen, "Peter", null);

Nu voert de ontwikkelaar zijn code uit en krijgt hij een NullPointerException.Daar zijn we, we moeten onze optionele parameter null controleren, wat ons oorspronkelijke doel verslaat om dit soort situaties te willen vermijden.

Hier zijn enkele mogelijkheden die we hadden kunnen doen om er beter mee om te gaan:

openbare statische lijst zoeken (lijst personen, tekenreeksnaam, geheel getal leeftijd) {// Null controleert op personen en naam final Geheel getal ageFilter = leeftijd! = null? leeftijd: 0; retourneer people.stream () .filter (p -> p.getName (). is gelijk aan (naam)) .filter (p -> p.getAge (). get ()> = ageFilter) .collect (Collectors.toList () ); }

Daar is de parameter nog steeds optioneel, maar we behandelen het in slechts één controle.

Een andere mogelijkheid zou zijn geweest creëer twee overbelaste methoden:

openbare statische lijst zoeken (lijst personen, tekenreeksnaam) {return doSearch (personen, naam, 0); } openbare statische lijst zoeken (lijst personen, string naam, int leeftijd) {return doSearch (mensen, naam, leeftijd); } private static List doSearch (List people, String name, int age) {// Null-controles voor personen en naam retourneren people.stream () .filter (p -> p.getName (). equals (name)) .filter ( p -> p.getAge (). get (). intValue ()> = leeftijd) .collect (Collectors.toList ()); }

Op die manier bieden we een duidelijke API met twee methoden die verschillende dingen doen (hoewel ze de implementatie delen).

Er zijn dus oplossingen die u niet kunt gebruiken Optioneels als methodeparameters. De bedoeling van Java bij het uitbrengen van Optioneel was om het te gebruiken als een retourtype, waarmee wordt aangegeven dat een methode een lege waarde kan retourneren. In feite is de praktijk van het gebruik Optioneel als methode-parameter wordt zelfs ontmoedigd door sommige code-inspecteurs.

16. Optioneel en serialisering

Zoals hierboven besproken, Optioneel is bedoeld om te worden gebruikt als een retourtype. Het wordt niet aanbevolen om het als een veldtype te gebruiken.

Bovendien, gebruik makend van Optioneel in een klasse die kan worden geserialiseerd, resulteert in een NotSerializableException. Ons artikel Java Optioneel as Return Type pakt de problemen met serialisering verder aan.

En in het gebruik van Optioneel Met Jackson leggen we uit wat er wanneer gebeurt Optioneel velden zijn geserialiseerd, samen met een aantal tijdelijke oplossingen om de gewenste resultaten te bereiken.

17. Conclusie

In dit artikel hebben we de meeste belangrijke functies van Java 8 besproken Optioneel klasse.

We hebben kort enkele redenen onderzocht waarom we ervoor zouden kiezen om te gebruiken Optioneel in plaats van expliciete null-controle en invoervalidatie.

We hebben ook geleerd hoe we de waarde van een Optioneel, of een standaard indien leeg, met de krijgen(), of anders() en orElseGet () methoden (en zag het belangrijke verschil tussen de laatste twee).

Toen zagen we hoe we onze Optioneels met kaart (), flatMap () en filter(). We bespraken wat een vloeiend APIOptioneel aanbiedingen, omdat het ons in staat stelt om de verschillende methoden gemakkelijk te koppelen.

Eindelijk zagen we waarom Optioneels als methodeparameters is een slecht idee en hoe je dit kunt vermijden.

De broncode voor alle voorbeelden in het artikel is beschikbaar op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found