null を判定するためにまだ != null を使用していますか? 新しい姿勢を教えてください

# 導入

記事の冒頭では、NPE 問題について説明します。NPE 問題とは、開発中によく遭遇する NullPointerException です。2 つのクラスがあり、それらの UML クラス図が次のとおりであると仮定します。

この場合、次のようなコードがあります

user.getAddress().getProvince();

この書き方では、user が null の場合に NullPointerException が発生する可能性があります。この問題を解決するために、次のような記述方法を採用します。

if(user!=null){
   
       Address address = user.getAddress();    if(address!=null){
   
           String province = address.getProvince();    }}

この種の文章は比較的醜いので、上記の醜い文章を回避するには、醜いデザインをエレガントにします。JAVA8 では、この記述方法を最適化するための Optional クラスが提供されています。これについては、次のテキストで詳しく説明します。

# APIの紹介

まず API について紹介します。他の記事とは異なり、この記事ではアナロジーを使用し、それをソース コードと組み合わせます。他の記事とは異なり、API が 1 つずつリストされているため、重要なポイントを見つけることができません。

1、任意(T値)、空()、of(T値)、ofNullable(T値)

これら 4 つの機能には相関関係があるため、記憶のためにまとめられます。

まず、Optional(T value)、つまりコンストラクターにはプライベート権限があり、外部から呼び出すことができないことを説明します。残りの 3 つの関数は、呼び出すためのパブリック権限です。そして、Optional の本質は、内部に実際の値を格納し、構築時にその値が空かどうかを直接判断することです。さて、少し抽象的になってしまいましたが。以下の図に示すように、Optional(T value) コンストラクターのソース コードに直接移動します。

すると、of(T値)のソースコードは以下のようになります。

public static <T> Optional<T> of(T value) {
   
       return new Optional<>(value);}

つまり、of(T value) 関数は内部でコンストラクターを呼び出します。コンストラクターのソース コードに基づいて、次の 2 つの結論を導き出すことができます。

  • of(T value) 関数を通じて構築された Optional オブジェクトは、Value 値が空の場合でも NullPointerException を報告します。

  • of(T value) 関数を通じて構築された Optional オブジェクトは、Value 値が空でない場合、通常は Optional オブジェクトを構築できます。

さらに、Optional クラスは、値が null のオブジェクトも維持します。これはおそらく次のようになります。

​​​​​​​

public final class Optional<T> {
    //省略....
    private static final Optional<?> EMPTY = new Optional<>();
    private Optional() {
        this.value = null;
    }
    //省略...
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
}

次に、 empty() の役割は EMPTY オブジェクトを返すことです。

さて、基礎ができたので、ofNullable(T value) の役割について話しましょう。ソース コードに行きましょう。

 public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

さて、誰もがそれが何を意味するかを理解する必要があります。of(T value) との違いは、値が null の場合、of(T value) は NullPointerException を報告しますが、ofNullable(T value) は Exception をスローせず、ofNullable(T value) は直接 EMPTY オブジェクトを返すことです。

それは、プロジェクトでは関数ではなく ofNullable 関数のみを使用するという意味ですか?

いや、存在する以上、当然価値がある。実行中は、NullPointerException を非表示にしたくありません。代わりに、直ちに報告する必要があり、その場合には Of 関数が使用されます。しかし、そのような場面は本当に稀であることを認めざるを得ません。ブロガーは、junit テスト ケースを作成するときにのみこの関数を使用しました。

2、orElse(T other)、orElseGet(Supplier<? extends T> other)およびorElseThrow(Supplier<? extends X>ExceptionSupplier)

これら 3 つの関数はメモリ用にまとめられており、コンストラクターに渡された値が null の場合にすべて呼び出されます。orElse と orElseGet の使用法は次のとおりです。これは、値が null の場合にデフォルト値を与えるのと同じです。

@Test
public void test() {
    User user = null;
    user = Optional.ofNullable(user).orElse(createUser());
    user = Optional.ofNullable(user).orElseGet(() -> createUser());
}
public User createUser(){
    User user = new User();
    user.setName("zhangsan");
    return user;
}

これら 2 つの関数の違い: ユーザー値が null でない場合でも、orElse 関数は createUser() メソッドを実行しますが、orElseGet 関数は createUser() メソッドを実行しません。これは自分でテストできます。

orElseThrow は値が null の場合は直接例外をスローします。

User user = null;
Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));


3、map(Function<? super T, ? extends U> mapper)和flatMap(Function<? super T, Optional<U>> mapper)

これら 2 つの関数はメモリのセットに配置されており、これら 2 つの関数が行うことは値の変換です。

ソースコードを直接アップロード

 public final class Optional<T> {
    //省略....
     public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    //省略...
     public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }
}

関数本体では、これら 2 つの関数に違いはありません。唯一の違いは入力パラメータです。map 関数で受け入れられる入力パラメータのタイプは Function<? super T, ? extends U> ですが、flapMap で受け入れられる入力パラメータのタイプは Function<? super T, Optional<U>> です。

具体的な使用法としては、マップの場合:

ユーザー構成が以下の場合

public class User {
    private String name;
    public String getName() {
        return name;
    }
}

このときの名前の書き方は以下の通りです。

String city = Optional.ofNullable(user).map(u-> u.getName()).get();

フラットマップの場合:

ユーザー構成が以下の場合

public class User {
    private String name;
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
}

このときの名前の書き方は以下の通りです。

String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();

4、isPresent()とifPresent(Consumer<? super T> Consumer)

これら 2 つの関数は一緒に記憶され、isPresent は値が空かどうかを判断し、ifPresent は値が空でない場合に何らかの操作を実行します。これら 2 つの関数のソースコードは次のとおりです

public final class Optional<T> {
    //省略....
    public boolean isPresent() {
        return value != null;
    }
    //省略...
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
}

追加の説明が必要なのは、誰もが入れてはいけないということです

if (user != null){
   // TODO: do something
}

として書かれます

User user = Optional.ofNullable(user);
if (Optional.isPresent()){
   // TODO: do something
}

このように書かれているため、コード構造は依然として醜いままです。正しい書き方は後ほどブロガーが教えてくれます。

ifPresent(Consumer<? super T>consumer) についても、以下に示すように使い方は非常に簡単です。

Optional.ofNullable(user).ifPresent(u->{
    // TODO: do something
});

5、フィルター(述語<?スーパーT>述語)

あまり言うことはありません。ソースコードにアクセスしてください。

public final class Optional<T> {
    //省略....
   Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
}

filter メソッドは、Optional に含まれる値をフィルターするための Predicate を受け入れます。含まれている値が条件を満たしている場合は、Optional が返されます。そうでない場合は、Optional.empty が返されます。

使い方は以下の通り

Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);

上に示したように、ユーザー名の長さが 6 文字未満の場合は、その名前が返されます。6 より大きい場合は、EMPTY オブジェクトが返されます。

# 実用化

例一

関数メソッド内

以前の書き方

public String getCity(User user)  throws Exception{
        if(user!=null){
            if(user.getAddress()!=null){
                Address address = user.getAddress();
                if(address.getCity()!=null){
                    return address.getCity();
                }
            }
        }
        throw new Excpetion("取值错误"); 
    }

JAVA8の記述方法

public String getCity(User user) throws Exception{
    return Optional.ofNullable(user)
                   .map(u-> u.getAddress())
                   .map(a->a.getCity())
                   .orElseThrow(()->new Exception("取指错误"));
}

例二

たとえば、メインプログラムでは

以前の書き方

if(user!=null){
    dosomething(user);
}

JAVA8の記述方法

 Optional.ofNullable(user)
    .ifPresent(u->{
        dosomething(u);
});

例 3

以前の書き方

public User getUser(User user) throws Exception{
    if(user!=null){
        String name = user.getName();
        if("zhangsan".equals(name)){
            return user;
        }
    }else{
        user = new User();
        user.setName("zhangsan");
        return user;
    }
}

Java8の書き方

public User getUser(User user) {
    return Optional.ofNullable(user)
                   .filter(u->"zhangsan".equals(u.getName()))
                   .orElseGet(()-> {
                        User user1 = new User();
                        user1.setName("zhangsan");
                        return user1;
                   });
}

他の例は 1 つずつ記載しません。ただし、このチェーン プログラミングを使用すると、コードはよりエレガントになります。ただし、ロジックはそれほど明白ではなく、可読性は低下しますが、状況に応じてプロジェクトで適切に使用できます。

おすすめ

転載: blog.csdn.net/yzs2022/article/details/130045282