Рубрики
Без рубрики

Java Необязательно в качестве возвращаемого типа

Узнайте, как и когда возвращать необязательный тип в Java.

Автор оригинала: baeldung.

1. введение

Тип Необязательный был введен в Java 8. Он обеспечивает четкий и явный способ передачи сообщения о том, что значения может не быть, без использования null .

При получении Необязательного возвращаемого типа мы, скорее всего, проверим, отсутствует ли значение, что приведет к меньшему количеству NullPointerException s в приложениях. Однако тип Необязательно подходит не во всех местах.

Хотя мы можем использовать его везде, где сочтем нужным, в этом уроке мы сосредоточимся на некоторых лучших практиках использования Optional в качестве возвращаемого типа.

2. Необязательно в качестве типа возврата

Тип Необязательный может быть возвращаемым типом для большинства методов, за исключением некоторых сценариев, обсуждаемых далее в учебнике.

В большинстве случаев возврат Необязательного просто прекрасен:

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

Это удобно, так как мы можем использовать Необязательный API в вызывающем методе:

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

Это также подходит для статического метода или служебного метода, чтобы вернуть Необязательное значение. Однако есть много ситуаций, когда мы не должны возвращать необязательный тип .

3. Когда Не Возвращаться Необязательно

Поскольку Optional является оболочкой и классом на основе значений , существуют некоторые операции, которые не могут быть выполнены с Optional объектом. Во многих случаях просто лучше возвращать фактический тип, а не Необязательный тип.

Вообще говоря, для желобов в POJOs более целесообразно возвращать фактический тип, а не Необязательный тип. В частности, важно, чтобы компоненты сущностей, модели данных и DTO имели традиционные геттеры.

Ниже мы рассмотрим некоторые важные примеры использования.

3.1. Сериализация

Давайте представим, что у нас есть простая сущность:

public class Sock implements Serializable {
    Integer size;
    Optional pair;

    // ... getters and setters
}

На самом деле это вообще не сработает. Если бы мы попытались сериализовать это, мы получили бы NotSerializableException :

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

И действительно, хотя сериализация Необязательно может работать с другими библиотеками , это, безусловно, добавляет ненужную сложность.

Давайте рассмотрим еще одно приложение с таким же несоответствием сериализации, на этот раз с JSON.

3.2. JSON

Современные приложения постоянно преобразуют объекты Java в JSON. Если геттер возвращает Необязательный тип, мы, скорее всего, увидим некоторую неожиданную структуру данных в окончательном JSON.

Допустим, у нас есть боб с необязательным свойством:

private String firstName;

public Optional getFirstName() {
    return Optional.ofNullable(firstName);
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

Итак, если мы используем Джексона для сериализации экземпляра Необязательно , мы получим:

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

Но на самом деле мы бы хотели, чтобы:

{"firstName":"Baeldung"}

Таким образом, Необязательно является проблемой для случаев использования сериализации. Далее давайте рассмотрим двоюродный брат сериализации: запись данных в базу данных.

3.3. JPA

В JPA геттер, сеттер и поле должны иметь имя, а также тип соглашения. Например, имя поле типа Строка должно быть сопряжено с геттером с именем getFirstName , который также возвращает Строку.

Следование этому соглашению упрощает несколько вещей, включая использование отражения библиотеками, такими как Hibernate, чтобы обеспечить нам отличную поддержку объектно-реляционного отображения.

Давайте взглянем на наш же вариант использования необязательного имени в POJO.

На этот раз, однако, это будет объект JPA:

@Entity
public class UserOptionalField implements Serializable {
    @Id
    private long userId;

    private Optional firstName;

    // ... getters and setters
}

И давайте продолжим и попробуем это сделать:

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

К сожалению, мы столкнулись с ошибкой:

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

Мы могли бы попробовать отклониться от этого стандарта. Например, мы могли бы сохранить свойство в виде Строки , но изменить геттер:

@Column(nullable = true) 
private String firstName; 

public Optional getFirstName() { 
    return Optional.ofNullable(firstName); 
}

Похоже, что у нас могут быть оба способа: иметь Необязательный тип возврата для геттера и постоянное поле FirstName .

Однако теперь, когда мы несовместимы с нашим геттером, сеттером и полем, будет сложнее использовать инструменты по умолчанию JPA и исходный код IDE.

До тех пор, пока JPA не получит элегантную поддержку типа Optional , мы должны придерживаться традиционного кода. Это проще и лучше:

private String firstName;

// ... traditional getter and setter

Давайте, наконец, посмотрим, как это влияет на внешний интерфейс – проверьте, не кажется ли проблема, с которой мы сталкиваемся, знакомой.

3.4. Языки выражений

Подготовка DTO для интерфейса представляет аналогичные трудности.

Например, давайте представим, что мы используем шаблон JSP для чтения нашего Пользователя Необязательно DTO имя из запроса:


Поскольку это Необязательно , мы не увидим ” Baeldung “. Вместо этого мы увидим String представление Необязательного типа:

Optional[Baeldung]

И это проблема не только с JSP. Любой язык шаблонов, будь то Velocity, Freemarker или что-то еще, должен будет добавить поддержку для этого. До тех пор давайте продолжим упрощать наши DTO.

4. Заключение

В этом уроке мы узнали, как мы можем возвращать Необязательный объект и как обращаться с такого рода возвращаемым значением.

С другой стороны, мы также узнали, что есть много сценариев, в которых нам было бы лучше не использовать Необязательный тип возврата для геттера. Хотя мы можем использовать тип Optional в качестве намека на то, что может не быть ненулевого значения, мы должны быть осторожны, чтобы не злоупотреблять типом Optional return, особенно в геттере компонента сущности или DTO.

Исходный код примеров в этом руководстве можно найти на GitHub .