Java戻り型としてオプション

1. 前書き

_https://www.baeldung.com/java-optional [Optional] _タイプはJava 8で導入されました。 _null_を使用せずに、値がない可能性があるというメッセージを伝える明確で明示的な方法を提供します。
_Optional_戻り値の型を取得する場合、値が欠落しているかどうかを確認する可能性が高く、アプリケーションの__NullPointerException__sが少なくなります。 ただし、_Optional_タイプはすべての場所に適しているわけではありません。
適切であればどこでも使用できますが、このチュートリアルでは、_Optional_を戻り値の型として使用するベストプラクティスに焦点を当てます。

2. Optional as Return Type

_Optional_型は、チュートリアルの後半で説明するいくつかのシナリオを除き、ほとんどのメソッドの戻り値型になります。
ほとんどの場合、_Optional_を返すのは問題ありません。
public static Optional<User> findUserByName(String name) {
    User user = usersByName.get(name);
    Optional<User> opt = Optional.ofNullable(user);
    return opt;
}
呼び出しメソッドで_Optional_ APIを使用できるため、これは便利です。
public static void changeUserName(String oldFirstName, String newFirstName) {
    findUserByFirstName(oldFirstName).ifPresent(user -> user.setFirstName(newFirstName));
}
静的メソッドまたはユーティリティメソッドが_Optional_値を返すことも適切です。 ただし、_an Optional_型を返すべきではない多くの状況があります。

3. 返さない場合_オプション_

_Optional_はラッパーであり、https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html [value-based]クラスであるため、実行できない操作がいくつかあります_Optional_オブジェクトに対して実行されます。 多くの場合、_Optional_型ではなく、実際の型を返す方が簡単です。
一般的に、POJOのゲッターでは、_Optional_型ではなく、実際の型を返す方が適しています。 特に、エンティティBean、データモデル、およびDTOが従来のゲッターを持つことが重要です。
以下にいくつかの重要なユースケースを検討します。

3.1. 直列化

単純なエンティティがあると想像してみましょう。
public class Sock implements Serializable {
    Integer size;
    Optional<Sock> pair;

    // ... getters and setters
}
*これは実際にはまったく機能しません。*これをシリアル化しようとすると、_NotSerializableException_が発生します。
new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(new Sock());
そして実際には、https://www.baeldung.com/jackson-optional [serializing _Optional_は他のライブラリで動作する可能性があります]、*それは確かに不要な複雑さを追加します*。
この同じシリアル化の不一致の別のアプリケーション、今回はJSONを見てみましょう。

3.2. JSON

最新のアプリケーションは、Javaオブジェクトを常にJSONに変換します。 ゲッターが_Optional_タイプを返す場合、最終的なJSONに予期しないデータ構造が表示される可能性が高くなります。
オプションのプロパティを持つabeanがあるとしましょう:
private String firstName;

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

public void setFirstName(String firstName) {
    this.firstName = firstName;
}
したがって、Jacksonを使用して_Optional_のインスタンスをシリアル化すると、次のようになります。
{"firstName":{"present":true}}
しかし、私たちが本当に欲しいのは:
{"firstName":"Baeldung"}
そのため、_オプション_は、シリアル化の使用例の苦痛です。 次に、シリアル化のいとこを見てみましょう:*データベースへのデータの書き込み*

3.3. JPA

JPAでは、ゲッター、セッター、およびフィールドには名前と型の一致が必要です。 たとえば、__String ___typeの__firstName __fieldフィールドは、also_String._を返す_getFirstName_というゲッターとペアにする必要があります。
この規則に従うと、Hibernateなどのライブラリによるリフレクションの使用など、いくつかのことがより簡単になり、優れたオブジェクトリレーショナルマッピングがサポートされます。
  • POJOのオプションの名の同じ使用例を見てみましょう。*

    ただし、今回はJPAエンティティになります。
@Entity
public class UserOptionalField implements Serializable {
    @Id
    private long userId;

    private Optional<String> 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.<clinit>(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)]
**この標準から逸脱してみることができます。 **たとえば、プロパティをa__String_のままにして、ゲッターを変更できます。
@Column(nullable = true)
private String firstName;

public Optional<String> getFirstName() {
    return Optional.ofNullable(firstName);
}
両方の方法がある可能性があります。ゲッターの_Optional_戻り値型と永続フィールド_firstName_を持ちます。
ただし、ゲッター、セッター、およびフィールドと矛盾しているため、JPAのデフォルトとIDEソースコードツールを活用することはより困難になります。
JPAが_Optional_型をエレガントにサポートするまでは、従来のコードに固執する必要があります。 よりシンプルで優れています。
private String firstName;

// ... traditional getter and setter
最後に、これがフロントエンドにどのように影響するかを見てみましょう。問題に遭遇した場合、おなじみの音がするかどうかを確認してください。

3.4. 式言語

フロントエンド用にDTOを準備する場合も同様の問題が発生します。
たとえば、JSPテンプレートを使用して、リクエストから_UserOptional_ DTOの_firstName_を読み取るとします。
<c:out value="${requestScope.user.firstName}" />
_Optional_であるため、「_ Baeldung_」は表示されません。 代わりに、_Optional_型の_String_表現が表示されます。
Optional[Baeldung]
そして、これはJSPだけの問題ではありません。 Velocity、Freemarker、またはその他のテンプレート言語であれば、このサポートを追加する必要があります。 それまでは、DTOをシンプルに保ち続けましょう。

4. 結論

このチュートリアルでは、_Optional_オブジェクトを返す方法と、この種の戻り値を処理する方法を学びました。
一方で、ゲッターに_Optional_戻り値型を使用しない方が良いと思われる多くのシナリオがあることも学びました。 null以外の値が存在しない可能性があるというヒントとして_Optional_タイプを使用できますが、特にエンティティBeanまたはDTOのゲッターでは、_Optional_戻り値タイプを使いすぎないように注意する必要があります。
このチュートリアルの例のソースコードは、https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-optional [GitHub]にあります。