NPE を回避するためのオプションの合理的な使用

1. オプションとは何ですか

Java で発生する可能性が最も高い例外はどのようなものですか。それは NullPointerException です。Null ポインタは時限爆弾のようなもので、常に何らかのトラブルをもたらします。開発プロセスでは、次のような事態を防ぐために Null 値を判断する必要が生じます。以前は null ポインタ。その方法は、例外をスローするか、または Optional が登場するまで if{}else{} のいずれかであり、NPE 問題をよりエレガントに解決できます。{}else{}
まず、「Optional」の著者である Brian Goetz による API の説明を見てみましょう。

私たちの目的は、「結果なし」を表す明確な方法が必要なライブラリ メソッドの戻り値の型に限定されたメカニズムを提供することであり、そのような目的で null を使用するとエラーが発生する可能性が圧倒的に高くなります。

翻訳の一般的な意味は次のとおりです。 Optional は、クラス ライブラリ メソッドの戻り値が値の有無を明確に表現できるようにする限定されたメカニズムを提供し、多くの場合 null によって引き起こされるエラーを回避します。そのため、ツール クラスを使用するようにしてください。 Java8 以降ではオプションであり、特定のシナリオのロジックでコードを簡素化します

2. オプションの一般的な方法は何ですか

静的メソッド

  • Optional.of (T 値): 指定された値に null 以外の値を指定するオプションを作成します。受信パラメータを null にすることはできません。そうでない場合は、NullPointerException がスローされます。
  • Optional.ofNullable() : 指定された値の Optional オブジェクトを作成します。指定されたパラメーターが null の場合、例外はスローされず、空の Optional オブジェクトが直接返されます。

オブジェクトメソッド

  • isPresent() : オプションが空かどうかを判断し、空の場合は false を返し、そうでない場合は true を返します。
  • get() : 値がある場合は Optional を返し、値がない場合は NoSuchElementException をスローします。
  • ifPresent(Consumer c) : オプションが空でない場合は、オプション内のオブジェクトを Consumer 関数に渡します。 orElse(T other) : オプションが空でない場合は、オプション内のオブジェクトを返します。それが null の場合は、その他を返します。これはデフォルト値です
  • orElseGet(Supplier other) :Optional が空でない場合は、Optional のオブジェクトを返します。null の場合は、Supplier 関数を使用してデフォルト値 other を生成します。
  • orElseThrow(SupplierException) : オプションが空でない場合は、オプションのオブジェクトを返します。それが null の場合は、サプライヤー関数によって生成された例外をスローします。
  • filter(Predicate p) : オプションが空でない場合はアサーション関数 p を実行し、p の結果が true の場合は元のオプションを返し、それ以外の場合は空のオプションを返します。
  • map(Function<T, U> マッパー) : オプションが空でない場合、オプションのオブジェクト t を別のオブジェクト u にマップし、u を新しいオプションのコンテナに格納します。
  • flatMap(Function<T,Optional<U>> マッパー) : 上記と同様、オプションが空でない場合、オブジェクト t を別のオプションにマップします。違い: マップは自動的に u をオプションに置きますが、 flatMap は手動でオプションを作成する必要があります。 Uのための

3. オプションの悪用シナリオ

1. if チェックに isPresent() を直接使用する

このように書くことと空間を直接判断することに違いはありませんが、手間が増えます

public class User{
    
    
	private String name;
	private Integer age;

	public String getName() {
    
    
		return name;
	}

	public void setName(String name) {
    
    
		this.name = name;
	}
}
// 错误使用Optional
	User u=null;
	Optional<User> userOpt=Optional.ofNullable(u);
	if(userOpt.isPresent()){
    
    
		String name=u.getName();
		System.out.println(name);
	}
	// 直接判断
	if(u !=null){
    
    
		String name=u.getName();
		System.out.println(name);
	}

使用可能な記述方法: ifPresent(Consumer<? super T> Consumer) を使用して Optional に値があるかどうかを判断し、値がある場合はコンシューマを実行し、値がない場合は何もしないことを推奨します。 isPresent() を直接 if チェックして判断する

userOpt.ifPresent(user -> System.out.println(user.getName()));

実際、条件が満たされているかどうかを判断するために、ストリーム処理の最後に isPresent() を使用することをお勧めします。

list.stream()
    .filer(x -> Objects.equals(x,param))
    .findFirst()
    .isPresent()

2. メソッドパラメータまたはPOJOでOptionalを使用する

Optional 自体はシリアル化を実装しておらず、既存の JSON シリアル化フレームワークのほとんどはシリアル化をサポートしていません。

public class User {
    
    
    private String name;
	private Integer age;
    // 不建议
    private Optional<String> address;
}

3. Optional.get を直接使用する

Optional は、空の判定や例外処理には役立ちません。コード内で Optional.get() を直接使用すると、空の判定を行わないのと同じくらい危険です。値がない場合は、NoSuchElementException がスローされます。

User u=null;
Optional<User> userOpt=Optional.ofNullable(u);
// 不建议
System.out.println(userOpt.get().getName());

結果は、
ここに画像の説明を挿入
値を取得するには 3 つの方法があります: get()、orElse()、orElseGet()
get() は直接使用できません。空の判定と組み合わせて使用​​する必要があります。これは実際には !=null と大きな違いはなく、表現と抽象化が改善されただけです。
orElse() と ElseGet() の違い
パフォーマンスの問題: 括弧内の内容はとにかく実行されます。 orElseGet() はメイン値が空の場合にのみ実行されます。 orElseGet() はサプライヤーを構築する必要があります。 不適切な使用、再び null ポインター
、 orElseのパラメータが間接的に計算できる場合。このステートメントは少しこじつけですが (null ポインター例外を引き起こすのは orElse ではないため)、orElseGet を使用すると、実際にこの状況を回避できます。

class User {
    
    
    // 中文名
	private String chineseName;
	// 英文名
	private EnglishName englishName;
}

class EnglishName {
    
    
    // 全名
    private String fullName;
    // 简写
    private String shortName;
}

User クラスがある場合、ユーザーがアカウントを登録するときに、自分の中国語名または英語名、あるいはその両方を入力する必要があるため、英語名のフルネームと略称を含む EnglishName クラスを抽象化します。英語の名前は本当に長すぎます)。ここで、次のように実装できる User#getName() メソッドを用意したいと考えています。ユーザーが中国語の名前のみを指定した場合、この時点では englishName 属性は null ですが、orElse では、englishName.getShortName() は常に englishName 属性になります。実行されます。getName2() では、このリスクはありません。

class User {
    
    
    // ... 之前的内容
    public String getName1() {
    
    
        return Optional.ofNullable(chineseName)
                .orElse(englishName.getShortName());
    }
    public String getName2() {
    
    
        return Optional.ofNullable(chineseName)
                .orElseGet(() -> englishName.getShortName());
    }
}

単に静的リソースや文字列などを返す場合は、静的リソースを直接返して orElse() を使用できます。

4. 注入されたプロパティで使用される

// 一般不建议
public class CommonService {
    
    
    private Optional<UserService> userService;
}

第四に、オプション姿勢の正しい使用

1. オブジェクトのプロパティを別のエンティティ オブジェクトに割り当てる

// 调用服务层查询数据库获得目标对象
ProdSku prodSku = prodSKUService.getFirstProdSku(p.getProdId());
if (prodSku != null) {
    
    
    prodAPP.setPrice(prodSku.getPrice());
}
// 使用Optional 和函数式编程,一次完成
Optional.ofNullable(prodSku).ifPresent(p -> prodAPP.setPrice(p.getPrice()));

2. 値が取得できない場合は、指定したデフォルトを返す

指定値が固定されている場合は、明示的に orElse() を使用することを推奨します。指定値があいまいな場合や多くの計算が必要な場合は、orElseGet() を使用することを推奨します。

2.1 コレクションのサイズを判断するが、コレクションが null であることを心配する

List<User> list=null;
Optional<List<User>> optional=Optional.ofNullable(list);
int size = optional.map(List::size).orElse(0);
// 输出0
System.out.println(size);
// 直接调用集合的size方法就有可能造成 NullPointerException 空指针异常
System.out.println(list.size());

2.2 エンティティクラスのgetメソッド初期化代入

// 当传给前端同学一个字段时候,并不希望这个值出现null,但数据库里确实就是null的情况
public BigDecimal getAccount() {
    
    
        if (account == null) {
    
    
            account = BigDecimal.ZERO;
        }
        return account;
}
// 代替:
public BigDecimal getAccount() {
    
    
        return Optional.ofNullable(account).orElse(BigDecimal.ZERO) ;
}

2.3 型間の変換、値がない場合はデフォルト値を返す

String price=null;
Double c=Optional.ofNullable(price).map(a->Double.valueOf(a)).orElse(0.00);
System.out.println(c);
// 直接转换必定空指针错误
Double b=Double.valueOf(price);

2.4 オブジェクトに対応する漢字情報を取得する

nullの場合は取得せずにそのまま戻ります aがnullであってもaの演算がnullであってもorElseに進むことができます

Optional<Integer> optional=Optional.ofNullable(super.getAduitStatus());
        return optional.map(a->AduitStatusEnum.name(String.valueOf(a))).orElse(null);

3. 多層属性の取得 (多層ネストされたエンティティの値の取得、利点の最も明らかな例)

 //+++++++++++++++++++++++模拟这个要取的对象从其他接口传过来是不是null++++++++++++++++++++++++++++++++++++
    InetSocketAddress inetSocketAddress = new InetSocketAddress(2);
    String re="小明取不到值";
    if(inetSocketAddress !=null){
    
    
        if(inetSocketAddress.getAddress() !=null){
    
    
            if(inetSocketAddress.getAddress().getHostName() !=null){
    
    
                String name2 = inetSocketAddress.getAddress().getHostName().toUpperCase();
                if(StringUtils.isNotBlank(name2) ){
    
    
                    re=name2;
                }
            }
        }
    }
    System.out.println(re);

    Optional<InetSocketAddress> optional=Optional.ofNullable(inetSocketAddress);
    String op= optional.map(a ->a.getAddress()).map(b ->b.getHostName()).map(c->c.toUpperCase()).orElse("小王也取不到值");
    System.out.println(op);

 //+++++++++++++++++++++++模拟这个要取的对象从其他接口传过来是null++++++++++++++++++++++++++++++++++++
    String re2="小明取不到值";
    InetSocketAddress inetSocketAddress2 = null;
    if(inetSocketAddress2 !=null){
    
    
        if(inetSocketAddress2.getAddress() !=null){
    
    
            if(inetSocketAddress2.getAddress().getHostName() !=null){
    
    
                String name2 = inetSocketAddress2.getAddress().getHostName().toUpperCase();
                if(name2 ==null){
    
    
                    re2=name2;
                }
            }
        }
    }
    System.out.println(re2);

    Optional<InetSocketAddress> optional2=Optional.ofNullable(inetSocketAddress2);
    String op2= optional2.map(a ->a.getAddress()).map(b ->b.getHostName()).map(c->c.toUpperCase()).orElse("小王也取不到值");
    System.out.println(op2);

実行結果は以下の図に示されています
ここに画像の説明を挿入

4. ビジネスシナリオをブロックする場合に推奨

BigDecimal stock = Optional.ofNullable(lcStockObj).map(a -> a.getAssetNum())
                           .orElseThrow(() -> new RuntimeException("库存数据不存在,出入库失败"));

参考リンク:https://blog.csdn.net/rhythm_1/article/details/121716010

おすすめ

転載: blog.csdn.net/neusoft2016/article/details/130744787