Java по избор като тип на връщане

1. Въведение

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

Когато получаваме незадължителен тип връщане, вероятно ще проверим дали стойността липсва, което води до по-малко NullPointerException s в приложенията. Въпреки това, по избор вид не е подходящ за всички места.

Въпреки че можем да го използваме навсякъде, където сметнем за добре, в този урок ще се съсредоточим върху някои най-добри практики за използване на незадължително като тип връщане.

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 е обвивка и клас, базиран на стойността, има някои операции, които не могат да бъдат направени срещу незадължителен обект. Много пъти е просто просто да се върне действителният тип, а не незадължителен .

Най-общо казано, за гетери в POJO е по-подходящо да се върне действителният тип, а не незадължителен . По-специално е важно за Entity Beans, моделите за данни и DTO да имат традиционни гетери.

Ще разгледаме някои от важните случаи на употреба по-долу.

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

Нека си представим, че имаме проста същност:

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

Това всъщност няма да работи изобщо. Ако трябваше да опитаме и да сериализираме това, щяхме да получим NotSerializableException :

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

И наистина, докато сериализирането на Optional може да работи с други библиотеки, то със сигурност добавя ненужна сложност.

Нека да разгледаме друго приложение на същото несъответствие на сериализацията, този път с 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; }

Така че, ако използваме Джаксън, за да сериализираме екземпляр на Optional , ще получим:

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

Но това, което наистина бихме искали, е:

{"firstName":"Baeldung"}

Така че, незадължително е проблем за случаите на използване на сериализация. След това нека разгледаме братовчеда на сериализацията: запис на данни в база данни.

3.3. JPA

В JPA, getter, setter и полето трябва да имат име, както и споразумение за тип. Например, поле firstName от тип String трябва да бъде сдвоено с гетер , наречен getFirstName, който също връща String.

Спазването на тази конвенция улеснява няколко неща, включително използването на размисъл от библиотеки като 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 .

Сега обаче, когато сме в противоречие с нашия getter, setter и поле, ще бъде по-трудно да използваме JPA по подразбиране и IDE инструментите за изходен код.

Докато JPA няма елегантна поддръжка от незадължителен тип, трябва да се придържаме към традиционния код. Това е по-просто и по-добре:

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

Нека най-накрая да разгледаме как това се отразява на предния край - проверете дали проблемът, който срещаме, звучи познато.

3.4. Езици на изразяване

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

Например, нека си представим, че ние сме с помощта JSP шаблони, за да прочетете нашата UserOptional DTO на FirstName от искането:

Тъй като това е по избор , няма да видим „ Baeldung “. Вместо това ще видим представянето на String от незадължителен тип:

Optional[Baeldung] 

И това не е проблем само с JSP. Всеки език за шаблони, независимо дали е Velocity, Freemarker или нещо друго, ще трябва да добави поддръжка за това. Дотогава нека продължим да улесняваме нашите DTO.

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

В този урок научихме как можем да върнем незадължителен обект и как да се справим с този вид възвръщаема стойност.

От друга страна, също така научихме, че има много сценарии, при които е по-добре да не използваме незадължителен тип връщане за гетер. Въпреки че можем да използваме незадължителен тип като намек, че може да няма ненулева стойност, трябва да внимаваме да не злоупотребяваме с незадължителния тип връщане, особено в гетер на обект на обект или DTO.

Изходният код на примерите в този урок може да бъде намерен на GitHub.