Java Optioneel als Return Type

1. Inleiding

De Optioneel type is geïntroduceerd in Java 8. Het biedt een duidelijke en expliciete manier om de boodschap over te brengen dat er misschien geen waarde is, zonder nul.

Wanneer u een Optioneel retourneringstype, zullen we waarschijnlijk controleren of de waarde ontbreekt, wat leidt tot minder NullPointerExceptions in de applicaties. echter, de Optioneel type is niet overal geschikt.

Hoewel we het overal kunnen gebruiken waar we het nodig achten, zullen we ons in deze tutorial concentreren op enkele best practices voor het gebruik Optioneel als een retourtype.

2. Optioneel als Return Type

Een Optioneel type kan voor de meeste methoden een retourtype zijn, behalve voor enkele scenario's die later in de zelfstudie worden besproken.

Meestal retourneert u een Optioneel is prima:

public static Optioneel findUserByName (String naam) {User user = usersByName.get (naam); Optioneel opt = Optioneel.ofNullable (gebruiker); terugkeer opt; }

Dit is handig omdat we de Optioneel API in de aanroepende methode:

openbare statische ongeldige changeUserName (String oldFirstName, String newFirstName) {findUserByFirstName (oldFirstName) .ifPresent (user -> user.setFirstName (newFirstName)); }

Het is ook geschikt voor een statische methode of hulpprogrammamethode om een Optioneel waarde. Er zijn echter veel situaties waarin we niet zouden moeten terugkeren een Optioneel type.

3. Wanneer niet terug te keren Optioneel

Omdat Optioneel is een wrapper en op waarde gebaseerde klasse, er zijn enkele bewerkingen waartegen niet kan worden uitgevoerd Optioneel voorwerp. Vaak is het gewoon beter om het daadwerkelijke type te retourneren in plaats van een Optioneel type.

Over het algemeen is het voor getters in POJO's geschikter om het daadwerkelijke type te retourneren, niet een Optioneel type. In het bijzonder is het belangrijk voor Entity Beans, datamodellen en DTO's om traditionele getters te hebben.

We zullen hieronder enkele van de belangrijke use-cases onderzoeken.

3.1. Serialisatie

Laten we ons voorstellen dat we een eenvoudige entiteit hebben:

openbare klasse Sock implementeert Serializable {Integer size; Optioneel paar; // ... getters en setters}

Dit werkt eigenlijk helemaal niet. Als we dit zouden proberen te serialiseren, zouden we een NotSerializableException:

nieuwe ObjectOutputStream (nieuwe ByteArrayOutputStream ()). writeObject (nieuwe Sock ()); 

En echt, tijdens het serialiseren Optioneel kan samenwerken met andere bibliotheken, het voegt zeker een onnodige complexiteit toe.

Laten we eens kijken naar een andere toepassing met dezelfde niet-overeenkomende serialisatie, dit keer met JSON.

3.2. JSON

Moderne applicaties converteren Java-objecten voortdurend naar JSON. Als een getter een Optioneel type, zullen we hoogstwaarschijnlijk een onverwachte datastructuur zien in de uiteindelijke JSON.

Laten we zeggen dat we een boon hebben met een optionele eigenschap:

private String voornaam; openbaar Optioneel getFirstName () {retourneer Optioneel.ofNullable (voornaam); } public void setFirstName (String firstName) {this.firstName = firstName; }

Dus als we Jackson gebruiken om een ​​instantie van Optioneel, We zullen krijgen:

{"firstName": {"present": true}} 

Maar wat we echt zouden willen is:

{"firstName": "Baeldung"}

Zo, Optioneel is lastig voor gebruikssituaties van serialisatie. Laten we vervolgens eens kijken naar de neef van serialisatie: het schrijven van gegevens naar een database.

3.3. JPA

In JPA moeten de getter, setter en het veld zowel naam als type overeenkomst hebben. Bijvoorbeeld een Voornaam veld van het type Draad moet worden gepaard met een getter genaamd getFirstName dat geeft ook een Draad.

Het volgen van deze conventie maakt verschillende dingen eenvoudiger, waaronder het gebruik van reflectie door bibliotheken zoals Hibernate, om ons uitstekende ondersteuning voor Object-Relational mapping te bieden.

Laten we eens kijken naar dezelfde use case van een optionele voornaam in een POJO.

Deze keer is het echter een PPV-entiteit:

@Entity openbare klasse UserOptionalField implementeert Serializable {@Id privé lange userId; privé Optioneel voornaam; // ... getters en setters}

En laten we doorgaan en proberen het vol te houden:

UserOptionalField gebruiker = nieuwe UserOptionalField (); gebruiker.setUserId (1l); user.setFirstName (Optioneel.of ("Baeldung")); entiteitManager.persist (gebruiker);

Helaas komen we een fout tegen:

Veroorzaakt door: javax.persistence.PersistenceException: [PersistenceUnit: com.baeldung.optionalReturnType] Kan Hibernate SessionFactory niet bouwen op org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException (EntityManagerFpa orghibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException (EntityManagerFpa orghibernate.java) .boot.internal.EntityManagerFactoryBuilderImpl.build (EntityManagerFactoryBuilderImpl.java:941) op org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory (HibernatePersistenceProvider.java:56) op javax.persistence.Persistence.createEntityManagerFactory (HibernatePersistenceProvider.java:56) persistence.Persistence.createEntityManagerFactory (Persistence.java:54) op com.baeldung.optionalReturnType.PersistOptionalTypeExample. (PersistOptionalTypeExample.java:11) Veroorzaakt door: org.hibernate.MappingException: Kon type niet bepalen voor: java.util.Optional tabel: UserOptionalField, voor kolommen: [org.hibernate.mapping.Column (firstName)]

We kunnen proberen afwijken van deze norm. We zouden het pand bijvoorbeeld kunnen houden als een Draad, maar verander de getter:

@Column (nullable = true) private String firstName; openbaar Optioneel getFirstName () {retourneer Optioneel.ofNullable (voornaam); }

Het lijkt erop dat we beide manieren kunnen hebben: een Optioneel retourtype voor de getter en een persistent veld Voornaam.

Nu we echter inconsistent zijn met onze getter, setter en veld, zal het moeilijker zijn om gebruik te maken van JPA-standaardinstellingen en IDE-broncodetools.

Totdat JPA elegante steun heeft van Optioneel type, we moeten vasthouden aan de traditionele code. Het is eenvoudiger en beter:

private String voornaam; // ... traditionele vangstof en zetter

Laten we eindelijk eens kijken hoe dit de front-end beïnvloedt - controleer of het probleem dat we tegenkomen bekend klinkt.

3.4. Expressietalen

Het voorbereiden van een DTO voor de front-end levert vergelijkbare problemen op.

Laten we ons bijvoorbeeld voorstellen dat we JSP-sjablonen gebruiken om onze Gebruiker Optioneel DTO's Voornaam van het verzoek:

Omdat het een Optioneel, we zullen niet zien "Baeldung“. In plaats daarvan zien we de Draad vertegenwoordiging van de Optioneel type:

Optioneel [Baeldung] 

En dit is niet alleen een probleem met JSP. Elke templating-taal, of het nu Velocity, Freemarker of iets anders is, moet hiervoor ondersteuning toevoegen. Laten we tot die tijd doorgaan met het simpel houden van onze DTO's.

4. Conclusie

In deze tutorial hebben we geleerd hoe we een Optioneel object, en hoe om te gaan met dit soort retourwaarde.

Aan de andere kant hebben we ook geleerd dat er veel scenario's zijn die we beter niet kunnen gebruiken Optioneel retourtype voor een getter. Terwijl we kunnen gebruiken Optioneel typ als een hint dat er mogelijk geen niet-null-waarde is, we moeten oppassen dat we de Optioneel retourneringstype, met name in een getter van een entity-bean of een DTO.

De broncode van de voorbeelden in deze tutorial is te vinden op GitHub.