Java Optional als Rückgabetyp

1. Einleitung

Der optionale Typ wurde in Java 8 eingeführt. Er bietet eine klare und explizite Möglichkeit, die Nachricht zu übermitteln, dass möglicherweise kein Wert vorhanden ist, ohne null zu verwenden .

Wenn Sie einen optionalen Rückgabetyp erhalten, prüfen wir wahrscheinlich, ob der Wert fehlt, was zu weniger NullPointerException in den Anwendungen führt. Der optionale Typ ist jedoch nicht an allen Orten geeignet.

Obwohl wir es verwenden können, wo immer wir es für richtig halten, konzentrieren wir uns in diesem Lernprogramm auf einige bewährte Methoden zur Verwendung von Optional als Rückgabetyp.

2. Optional als Rückgabetyp

Ein optionaler Typ kann für die meisten Methoden ein Rückgabetyp sein, mit Ausnahme einiger Szenarien, die später in diesem Lernprogramm erläutert werden.

In den meisten Fällen ist die Rücksendung einer Option in Ordnung:

public static Optional findUserByName(String name) { User user = usersByName.get(name); Optional opt = Optional.ofNullable(user); return opt; }

Dies ist praktisch, da wir die optionale API in der aufrufenden Methode verwenden können:

public static void changeUserName(String oldFirstName, String newFirstName) { findUserByFirstName(oldFirstName).ifPresent(user -> user.setFirstName(newFirstName)); }

Es ist auch für eine statische Methode oder eine Dienstprogrammmethode geeignet, einen optionalen Wert zurückzugeben. Es gibt jedoch viele Situationen, in denen wir keinen optionalen Typ zurückgeben sollten.

3. Wann Sie nicht zurückkehren können Optional

Da Optional ein Wrapper und eine wertebasierte Klasse ist, gibt es einige Operationen, die für ein optionales Objekt nicht ausgeführt werden können . Oft ist es einfach besser, den tatsächlichen Typ zurückzugeben, als einen optionalen Typ.

Im Allgemeinen ist es für Getter in POJOs besser geeignet, den tatsächlichen Typ zurückzugeben, nicht einen optionalen Typ. Insbesondere für Entity Beans, Datenmodelle und DTOs ist es wichtig, traditionelle Getter zu haben.

Im Folgenden werden einige wichtige Anwendungsfälle untersucht.

3.1. Serialisierung

Stellen wir uns vor, wir haben eine einfache Einheit:

public class Sock implements Serializable { Integer size; Optional pair; // ... getters and setters }

Das wird eigentlich gar nicht funktionieren. Wenn wir versuchen würden, dies zu serialisieren, würden wir eine NotSerializableException erhalten :

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

Die Serialisierung von Optional funktioniert zwar möglicherweise mit anderen Bibliotheken, fügt jedoch möglicherweise die möglicherweise unnötige Komplexität hinzu.

Werfen wir einen Blick auf eine andere Anwendung dieser Serialisierungsfehlanpassung, diesmal mit JSON.

3.2. JSON

Moderne Anwendungen konvertieren Java-Objekte ständig in JSON. Wenn ein Getter einen optionalen Typ zurückgibt , wird im endgültigen JSON höchstwahrscheinlich eine unerwartete Datenstruktur angezeigt.

Angenommen, wir haben eine Bean mit einer optionalen Eigenschaft:

private String firstName; public Optional getFirstName() { return Optional.ofNullable(firstName); } public void setFirstName(String firstName) { this.firstName = firstName; }

Wenn wir also Jackson verwenden, um eine Instanz von Optional zu serialisieren , erhalten wir:

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

Aber was wir wirklich wollen würden, ist:

{"firstName":"Baeldung"}

So Optional ist ein Schmerz für die Serialisierung Anwendungsfälle. Schauen wir uns als nächstes den Cousin der Serialisierung an: Schreiben von Daten in eine Datenbank.

3.3. JPA

In JPA sollten Getter, Setter und Feld sowohl eine Namens- als auch eine Typvereinbarung haben. Beispielsweise sollte ein Feld firstName vom Typ String mit einem Getter namens getFirstName gepaart werden , der auch einen String zurückgibt .

Das Befolgen dieser Konvention vereinfacht einige Dinge, einschließlich der Verwendung der Reflexion durch Bibliotheken wie Hibernate, um uns eine hervorragende Unterstützung für objektrelationale Zuordnungen zu bieten.

Schauen wir uns unseren gleichen Anwendungsfall eines optionalen Vornamens in einem POJO an.

Diesmal handelt es sich jedoch um eine JPA-Entität:

@Entity public class UserOptionalField implements Serializable { @Id private long userId; private Optional firstName; // ... getters and setters }

Und lassen Sie uns weitermachen und versuchen, es fortzusetzen:

UserOptionalField user = new UserOptionalField(); user.setUserId(1l); user.setFirstName(Optional.of("Baeldung")); entityManager.persist(user);

Leider stoßen wir auf einen Fehler:

Caused by: javax.persistence.PersistenceException: [PersistenceUnit: com.baeldung.optionalReturnType] Unable to build Hibernate SessionFactory at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1015) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:941) at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:56) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54) at com.baeldung.optionalReturnType.PersistOptionalTypeExample.(PersistOptionalTypeExample.java:11) Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Optional, at table: UserOptionalField, for columns: [org.hibernate.mapping.Column(firstName)]

Wir könnten versuchen , von diesem Standard abzuweichen. Zum Beispiel könnten wir die Eigenschaft als String behalten , aber den Getter ändern:

@Column(nullable = true) private String firstName; public Optional getFirstName() { return Optional.ofNullable(firstName); }

Es scheint, dass wir beide Möglichkeiten haben könnten: einen optionalen Rückgabetyp für den Getter und ein beständiges Feld firstName .

Da wir jedoch nicht mehr mit Getter, Setter und Feld übereinstimmen, wird es schwieriger sein, JPA-Standardeinstellungen und IDE-Quellcode-Tools zu nutzen.

Bis JPA eine elegante Unterstützung des optionalen Typs hat, sollten wir uns an den traditionellen Code halten. Es ist einfacher und besser:

private String firstName; // ... traditional getter and setter

Schauen wir uns zum Schluss an, wie sich dies auf das Frontend auswirkt. Überprüfen Sie, ob das Problem, auf das wir stoßen, bekannt vorkommt.

3.4. Ausdruckssprachen

Preparing a DTO for the front-end presents similar difficulties.

For example, let's imagine that we are using JSP templating to read our UserOptional DTO's firstName from the request:

Since it's an Optional, we'll not see “Baeldung“. Instead, we'll see the String representation of the Optional type:

Optional[Baeldung] 

And this isn't a problem just with JSP. Any templating language, be it Velocity, Freemarker, or something else, will need to add support for this. Until then, let's continue to keep our DTOs simple.

4. Conclusion

In this tutorial, we've learned how we can return an Optional object, and how to deal with this kind of return value.

Andererseits haben wir auch erfahren, dass es viele Szenarien gibt, in denen wir besser dran sind, den optionalen Rückgabetyp nicht für einen Getter zu verwenden. Während wir den optionalen Typ als Hinweis darauf verwenden können, dass möglicherweise kein Wert ungleich Null vorhanden ist, sollten wir darauf achten, den optionalen Rückgabetyp nicht zu stark zu verwenden, insbesondere in einem Getter einer Entity-Bean oder eines DTO.

Der Quellcode der Beispiele in diesem Tutorial finden Sie auf GitHub.