Java 8オプションの深さ

Java 8オプションの詳細

Java 8は、java.utilパッケージに新しいクラスOptionalを導入しました。 値の存在または不在を表すために使用されます。 この新しい構成の主な利点は、nullチェックとNullPointerExceptionが多すぎないことです。 ランタイムNullPointerExceptionsを回避し、クリーンでクリーンなJavaAPIまたはアプリケーションの開発をサポートします。 コレクションや配列と同様に、最大1つの値を保持するコンテナでもあります。 いくつかの有用な例を使用して、この新しい構成を調べてみましょう。

Java 8の利点オプション:

  1. Nullチェックは不要です。

  2. 実行時にこれ以上NullPointerExceptionはありません。

  3. クリーンできれいなAPIを開発できます。

  4. これ以上ボイラープレートコード

1. オプションの基本的な例

Optional.ofNullable()メソッドは、指定されたオブジェクトに値が存在する場合、空でないオプションを返します。 それ以外の場合は空を返します。

Optionaal.empty()メソッドは、空のオプションオブジェクトを作成するのに役立ちます。

OptionalBasicExample.java

package com.example;

import java.util.Optional;

public class OptionalBasicExample {

    public static void main(String[] args) {

        Optional gender = Optional.of("MALE");
        String answer1 = "Yes";
        String answer2 = null;

        System.out.println("Non-Empty Optional:" + gender);
        System.out.println("Non-Empty Optional: Gender value : " + gender.get());
        System.out.println("Empty Optional: " + Optional.empty());

        System.out.println("ofNullable on Non-Empty Optional: " + Optional.ofNullable(answer1));
        System.out.println("ofNullable on Empty Optional: " + Optional.ofNullable(answer2));

        // java.lang.NullPointerException
        System.out.println("ofNullable on Non-Empty Optional: " + Optional.of(answer2));

    }

}

出力

Non-Empty Optional:Optional[MALE]
Non-Empty Optional: Gender value : MALE
Empty Optional: Optional.empty

ofNullable on Non-Empty Optional: Optional[Yes]
ofNullable on Empty Optional: Optional.empty

Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at java.util.Optional.(Optional.java:96)
    at java.util.Optional.of(Optional.java:108)
    //...

2. Optional.map and flatMap

OptionalMapFlapMapExample.java

package com.example;

import java.util.Optional;

public class OptionalMapFlapMapExample {

    public static void main(String[] args) {

        Optional nonEmptyGender = Optional.of("male");
        Optional emptyGender = Optional.empty();

        System.out.println("Non-Empty Optional:: " + nonEmptyGender.map(String::toUpperCase));
        System.out.println("Empty Optional    :: " + emptyGender.map(String::toUpperCase));

        Optional> nonEmptyOtionalGender = Optional.of(Optional.of("male"));
        System.out.println("Optional value   :: " + nonEmptyOtionalGender);
        System.out.println("Optional.map     :: " + nonEmptyOtionalGender.map(gender -> gender.map(String::toUpperCase)));
        System.out.println("Optional.flatMap :: " + nonEmptyOtionalGender.flatMap(gender -> gender.map(String::toUpperCase)));

    }

}

出力

Non-Empty Optional:: Optional[MALE]
Empty Optional    :: Optional.empty
Optional value   :: Optional[Optional[male]]
Optional.map     :: Optional[Optional[MALE]]
Optional.flatMap :: Optional[MALE]

3. Optional.filter

OptionalFilterExample.java

package com.example;

import java.util.Optional;

public class OptionalFilterExample {

    public static void main(String[] args) {

        Optional gender = Optional.of("MALE");
        Optional emptyGender = Optional.empty();

        //Filter on Optional
        System.out.println(gender.filter(g -> g.equals("male"))); //Optional.empty
        System.out.println(gender.filter(g -> g.equalsIgnoreCase("MALE"))); //Optional[MALE]
        System.out.println(emptyGender.filter(g -> g.equalsIgnoreCase("MALE"))); //Optional.empty

    }

}

出力

Optional.empty
Optional[MALE]
Optional.empty

4. オプションのisPresentおよびifPresent

指定されたオプションオブジェクトが空でない場合、Optional.isPresent()はtrueを返します。 そうでなければfalseを返します。

Optional.ifPresent()は、指定されたオプションオブジェクトが空でない場合、指定されたアクションを実行します。 そうでなければfalseを返します。

OptionalIfPresentExample.java

package com.example;

import java.util.Optional;

public class OptionalIfPresentExample {

    public static void main(String[] args) {

        Optional gender = Optional.of("MALE");
        Optional emptyGender = Optional.empty();

        if (gender.isPresent()) {
            System.out.println("Value available.");
        } else {
            System.out.println("Value not available.");
        }

        gender.ifPresent(g -> System.out.println("In gender Option, value available."));

        //condition failed, no output print
        emptyGender.ifPresent(g -> System.out.println("In emptyGender Option, value available."));

    }

}

出力

Value available.
In gender Option, value available.

5. オプションのorElseメソッド

Optional Containerに存在する場合、値を返します。 それ以外の場合、指定されたデフォルト値を返します。

OptionalOrElseExample.java

package com.example;

import java.util.Optional;

public class OptionalOrElseExample {

    public static void main(String[] args) {

        Optional gender = Optional.of("MALE");
        Optional emptyGender = Optional.empty();

        System.out.println(gender.orElse("")); //MALE
        System.out.println(emptyGender.orElse("")); //

        System.out.println(gender.orElseGet(() -> "")); //MALE
        System.out.println(emptyGender.orElseGet(() -> "")); //

    }

}

出力

MALE

MALE

6. Java 8オプションなし

誰もがオンラインショッピングに精通しているからです。 有名なeコマースWebサイト用のモバイル製品モジュールを実装するとします。

Java 8オプションなしでモバイルドメインモジュールを実装しましょう。

ScreenResolution.java

package com.example.without.optional;

public class ScreenResolution {

    private int width;
    private int height;

    public ScreenResolution(int width, int height){
        this.width = width;
        this.height = height;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

}

DisplayFeatures.java

package com.example.without.optional;

public class DisplayFeatures {

    private String size; // In inches
    private ScreenResolution resolution;

    public DisplayFeatures(String size, ScreenResolution resolution){
        this.size = size;
        this.resolution = resolution;
    }

    public String getSize() {
        return size;
    }
    public ScreenResolution getResolution() {
        return resolution;
    }

}

Mobile.java

package com.example.without.optional;

public class Mobile {

    private long id;
    private String brand;
    private String name;
    private DisplayFeatures displayFeatures;
    // Likewise we can see Memory Features, Camera Features etc.

    public Mobile(long id, String brand, String name,
                            DisplayFeatures displayFeatures){
        this.id = id;
        this.brand = brand;
        this.name = name;
        this.displayFeatures = displayFeatures;
    }

    public long getId() {
        return id;
    }

    public String getBrand() {
        return brand;
    }

    public String getName() {
        return name;
    }

    public DisplayFeatures getDisplayFeatures() {
        return displayFeatures;
    }

}

ここで、getMobileScreenWidth()メソッドを観察すると、多くのnullチェックを含む多くのボイラープレートコードがあります。 Java 8より前のバージョンでは、ランタイムNullPointerExceptionsを回避するために、これらすべての意味のないことを行う必要があります。

MobileService.java

package com.example.without.optional;

public class MobileService {

    public int getMobileScreenWidth(Mobile mobile){

        if(mobile != null){
            DisplayFeatures dfeatures = mobile.getDisplayFeatures();
            if(dfeatures != null){
                ScreenResolution resolution = dfeatures.getResolution();
                if(resolution != null){
                    return resolution.getWidth();
                }
            }
        }
        return 0;

    }

}

これらのDomainオブジェクトをテストするテストアプリケーションを1つ開発します。

MobileTesterWithoutOptional.java

package com.example.without.optional;

public class MobileTesterWithoutOptional {

    public static void main(String[] args) {

        ScreenResolution resolution = new ScreenResolution(750,1334);
        DisplayFeatures dfeatures = new DisplayFeatures("4.7", resolution);
        Mobile mobile = new Mobile(2015001, "Apple", "iPhone 6s", dfeatures);

        MobileService mService = new MobileService();

        int mobileWidth = mService.getMobileScreenWidth(mobile);
        System.out.println("Apple iPhone 6s Screen Width = " + mobileWidth);

        ScreenResolution resolution2 = new ScreenResolution(0,0);
        DisplayFeatures dfeatures2 = new DisplayFeatures("0", resolution2);
        Mobile mobile2 = new Mobile(2015001, "Apple", "iPhone 6s", dfeatures2);
        int mobileWidth2 = mService.getMobileScreenWidth(mobile2);
        System.out.println("Apple iPhone 16s Screen Width = " + mobileWidth2);

    }

}

出力

Apple iPhone 6s Screen Width = 750
Apple iPhone 16s Screen Width = 0

7. Java 8オプション付き

今度は、Java 8オプションの構成を使用して、きれいできれいな方法で同じドメインモデルを開発します。

P.S ScreenResolution.java no change. 上記のセクションを参照してください。

DisplayFeatures.java

package com.example.with.optional;

import java.util.Optional;

public class DisplayFeatures {

    private String size; // In inches
    private Optional resolution;

    public DisplayFeatures(String size, Optional resolution){
        this.size = size;
        this.resolution = resolution;
    }

    public String getSize() {
        return size;
    }
    public Optional getResolution() {
        return resolution;
    }

}

Mobile.java

package com.example.with.optional;

import java.util.Optional;

public class Mobile {

    private long id;
    private String brand;
    private String name;
    private Optional displayFeatures;
    // Like wise we can see MemoryFeatures, CameraFeatures etc.
    // For simplicity, using only one Features

    public Mobile(long id, String brand, String name, Optional displayFeatures){
        this.id = id;
        this.brand = brand;
        this.name = name;
        this.displayFeatures = displayFeatures;
    }

    public long getId() {
        return id;
    }

    public String getBrand() {
        return brand;
    }

    public String getName() {
        return name;
    }

    public Optional getDisplayFeatures() {
        return displayFeatures;
    }

}

ここでは、nullチェックとボイラープレートコードなしでgetMobileScreenWidth()APIがどれほどクリーンであるかを観察できます。 実行時のNullPointerExceptionsについて心配する必要はありません。

MobileService.java

package com.example.with.optional;

import java.util.Optional;

public class MobileService {

  public Integer getMobileScreenWidth(Optional mobile){
    return mobile.flatMap(Mobile::getDisplayFeatures)
         .flatMap(DisplayFeatures::getResolution)
         .map(ScreenResolution::getWidth)
         .orElse(0);

  }

}

1つのテストコンポーネントを開発する

MobileTesterWithOptional.java

package com.example.with.optional;

import java.util.Optional;

public class MobileTesterWithOptional {

  public static void main(String[] args) {
    ScreenResolution resolution = new ScreenResolution(750,1334);
    DisplayFeatures dfeatures = new DisplayFeatures("4.7", Optional.of(resolution));
    Mobile mobile = new Mobile(2015001, "Apple", "iPhone 6s", Optional.of(dfeatures));

    MobileService mService = new MobileService();

    int width = mService.getMobileScreenWidth(Optional.of(mobile));
    System.out.println("Apple iPhone 6s Screen Width = " + width);

    Mobile mobile2 = new Mobile(2015001, "Apple", "iPhone 6s", Optional.empty());
    int width2 = mService.getMobileScreenWidth(Optional.of(mobile2));
    System.out.println("Apple iPhone 16s Screen Width = " + width2);
  }
}

出力

Apple iPhone 6s Screen Width = 750
Apple iPhone 16s Screen Width = 0

8. Java Optionalはどこに適合しますか?

上記のリアルタイム小売ドメインのユースケースを観察する場合、Java Optionalコンストラクトが次の場所で役立つことを知っておく必要があります。

8.1 Method Parameter

public void setResolution(Optional resolution) {
    this.resolution = resolution;
}

8.2 Method Return Type

public Optional getResolution() {
    return resolution;
}

8.3 Constructor Parameter

public DisplayFeatures(String size, Optional resolution){
    this.size = size;
    this.resolution = resolution;
}

8.4 Variable Declaration

private Optional resolution;

8.5 Class Level

public class B

public class A> { }

ソースコードをダウンロード

ダウンロード–Java8Optional-example.zip(4 KB)