153 測定値

Code Smell 304: Null Pointer Exception - Runtime Crash を引き起こす NULL 参照を回避する方法

Maximiliano Contieri9m2025/06/20
Read on Terminal Reader

長すぎる; 読むには

適切な検証および null-safe パターンを使用することで、ランタイム障害を引き起こす NULL 参照を回避する
featured image - Code Smell 304: Null Pointer Exception - Runtime Crash を引き起こす NULL 参照を回避する方法
Maximiliano Contieri HackerNoon profile picture

私はNULLの問題について書き続けていますが、毎日ニュースが私に思い出します:NULLはまだ生きています。


TL;DR: 適切な検証および null-safe パターンを使用することで、ランタイム障害を引き起こす NULL 参照を避ける

TL;DR: 適切な検証および null-safe パターンを使用することで、ランタイム障害を引き起こす NULL 参照を避ける

トラブル

  • ランタイムクラッシュ
  • 大規模な事故と中断
  • 予想外の行動
  • ハードデバッグ
  • ユーザの不満
  • システム不安定
  • 信頼性の低下

インGoogle クラウドケース:

  • 悪質なエラー処理: Null データを優雅に処理する代わりにコードが崩壊
  • No feature flags: New code was not gradually rolled out with safety controls. セキュリティコントロールで新しいコードが徐々にリリースされなかった。
  • Instant Global Replication: Bad Data Spreads Worldwide Immediately Like in Crowdstrike Incident (インスタントグローバル・レプリケーション:悪データがCrowdstrike Incidentのように世界中にすぐに広がる)
  • No Randomized Backoff: Recovery Caused Infrastructure Overload (ランダム化されたバックコフなし)
  • 不十分なテスト: デプロイ中に失敗シナリオがテストされたことはありません。

ソリューション↓

  1. ゼロを避ける
  2. null チェックを使用して、null があなたのコントロール外にある場合(例えば、外部 API)
  3. デフォルト値の初期化
  4. ガード条項の適用
  5. ゼロオブジェクトの使用
  6. オプションを使うな

改装 ️

https://hackernoon.com/code-refactoring-tips-no-015-remove-null

コンテキスト

平成25年6月12日 A大切な中断Google Cloud プラットフォームで実現しました。


Google CloudおよびGoogle Workspaceサービスの数十件に影響を及ぼし、世界中で午前10時49分から午後1時49分まで(合計3時間)、一部のサービスは完全に回復するのに時間がかかる。


この障害は、Google の API マネジメントシステムのカスカディング エラーによって引き起こされました。


  • トリガー :

2025年5月29日、Googleは「Service Control」(API管理システム)に新しいコードを展開し、追加の割引ポリシーチェックを追加しました。


このコードには重大な欠陥があり、適切な欠陥がありました。ミス操作保護されていなかったのは、フラッグ Flag.


  • 失敗:

6月12日(日) 空白を含む政策変更ゼロService Control がこれらの空白のフィールドを処理しようとすると、保護されていないコードパスに null ポインタに遭遇し、バイナリが無限のループで崩壊します。


  • グローバルな影響:

配分管理はグローバルであるため、この腐敗したデータは数秒で世界中で複製され、サービスコントロールがあらゆる地域で崩壊した。


ゼロポインタの例外は、存在しないオブジェクトの方法または属性にアクセスしようとすると発生します。


これは、変数が有効なオブジェクトインスタンスの代わりに null 参照を含んでいる場合に起こります。


この問題は、これらの例外がアプリケーションを崩壊させ、ユーザーを失望させることができる生産環境において特に危険になります。


Java、C#、JavaScriptなどの言語は特にこの問題に敏感ですが、現代の言語機能やパターンはこれらのクラッシュを完全に回避するのに役立ちます。


Nullsは何十年もの間、ソフトウェア業界で大きな問題となっていますが、ソフトウェアエンジニアは、開発者の警告にもかかわらず、それらを無視し続けています。

サンプルコード

Wrong ❌

public class ServiceControlPolicy {
  private SpannerDatabase spannerDB;
  private QuotaManager quotaManager;
    
  public void applyPolicyChange(PolicyChange change) {
      // NULL POINTER: change can be null
      Policy policy = spannerDB.getPolicy(change.getPolicyId());
      // NULL POINTER: policy can be null from the database
      String quotaField = policy.getQuotaField();
      // NULL POINTER: quotaField can be null (blank field)
      quotaManager.updateQuota(quotaField, change.getValue());
  }
    
  public void exerciseQuotaChecks(String region) {
      // NULL POINTER: policies list can be null
      List<Policy> policies = spannerDB.getPoliciesForRegion(region);
      for (Policy policy : policies) {
          // NULL POINTER: individual policy can be null
          String quotaValue = policy.getQuotaField();
          // NULL POINTER: quotaValue can be null before trim()
          quotaManager.checkQuota(quotaValue.trim());
      }
  }
    
  public boolean validatePolicyData(Policy policy) {
      // NULL POINTER: policy parameter can be null
      String quotaField = policy.getQuotaField();
      // NULL POINTER: quotaField can be null before length()
      return quotaField.length() > 0 && 
             !quotaField.equals("null");
  }
    
  public void replicateGlobally(PolicyChange change) {
      List<String> regions = getGlobalRegions();
      for (String region : regions) {
          // NULL POINTER: change.getPolicy() can return null
          spannerDB.insertPolicy(region, change.getPolicy());
      }
  }
}

正しい

public class ServiceControlPolicy {
  private SpannerDatabase spannerDB;
  private QuotaManager quotaManager;
    
  public void applyPolicyChange(PolicyChange change) {
      if (change == null) {
          // Assuming it comes from an external API
          // Beyond your control
          change = new NullPolicyChange();
      }
      
      Policy policy = findPolicyOrNull(change.policyId());
      String quotaField = policy.quotaField();
      if (!quotaField.isEmpty()) {
          quotaManager.updateQuota(quotaField, change.value());
      }
  }
    
  public void exerciseQuotaChecks(String region) {
      if (region == null || region.isEmpty()) {
          // Assuming it comes from an external API
          // Beyond your control
          return;
      }
      
      List<Policy> policies = policiesOrEmpty(region);
      
      for (Policy policy : policies) {
          String quotaValue = policy.quotaField();
          if (!quotaValue.isEmpty()) {
              quotaManager.checkQuota(quotaValue.trim());
          }
      }
  }
    
  public boolean validatePolicyData(Policy policy) {
      if (policy == null) {
          // Assuming it comes from an external API
          // Beyond your control
          // From now on, you wrap it
          policy = new NullPolicy();
      }
      
      String quotaField = policy.quotaField();
      return quotaField.length() > 0;
  }
    
  public void replicateGlobally(PolicyChange change) {
      if (change == null) {
          // Assuming it comes from an external API
          // Beyond your control
          // From now on, you wrap it
          change = new NullPolicyChange();
      }
        
      Policy policy = change.policy();
      if (policy == null) {
          // Assuming it comes from an external API
          // Beyond your control
          // From now on, you wrap it
          policy = new NullPolicy();
      }
        
      List<String> regions = globalRegions();
      for (String region : regions) {
          spannerDB.insertPolicy(region, policy);
      }
  }
    
  private Policy findPolicyOrNull(String policyId) {
      Policy policy = spannerDB.policy(policyId);
      return policy != null ? policy : new NullPolicy();
  }
    
  private List<Policy> policiesOrEmpty(String region) {
      List<Policy> policies = spannerDB.policiesForRegion(region);
      if (policies == null) {
          // This is a good NullObject
          return Collections.emptyList();
      }
      
      return policies.stream()
              .map(p -> p != null ? p : new NullPolicy())
              .collect(Collectors.toList());
  }
}

class NullPolicy extends Policy {
  @Override
  public String quotaField() { return ""; }
    
  @Override
  public String policyId() { return "unknown-policy"; }
    
  @Override
  public Map<String, String> metadata() { 
      return Collections.emptyMap(); 
  }
}

class NullPolicyChange extends PolicyChange {
  @Override
  public String policyId() { return ""; }
    
  @Override
  public String value() { return ""; }
    
  @Override
  public Policy policy() { return new NullPolicy(); }
}

検出

  • (x)半自動

Null チェックなしのオブジェクトで直接メソッド呼び出しのコードをレビューすることにより、潜在的な null ポインター例外を検出できます。


Linters は、返す可能性のある方法から返す値を調べることができます。ゼロ初期化されていないオブジェクトフィールドを検索し、潜在的な null dereferences を表示する静的分析ツールを使用します。


現代のアイデアはしばしば警告でこれらの問題を強調します。

タグ ️

  • ゼロ

レベル

  • (x) 中間

なんでバッテリーが大事なの️

IN THE現実世界物体は存在するか、存在しないか。


あなたのプログラムでこれを正しくモデリングすると、あなたは明確なOne-to-one メール現実とコードの間


null 参照を許可することによってこのバイジェクションを破ると、あなたのコードに存在するが、現実世界には存在しない幽霊オブジェクトが生成され、これらの存在しないエンティティとの相互作用を試みるときに崩壊をもたらします。


ライセンスプレートを「NULL」と名付けると、駐車券が多い

世代

AI ジェネレーターは、ハッピー パス シナリオに焦点を当てているため、ゼロ ポインタの脆弱性を持つコードを頻繁に作成します。


彼らはしばしば、オブジェクトが存在する可能性のあるエッジケースを考慮せずに方法呼び出しを生成する。ゼロ特に、複雑なオブジェクトの階層や、外部データソースに対処する場合。

検出 ↓↓

AI ツールは、防御プログラミングの実践に関する明確な指示を提供する場合に、ゼロポインタの問題を検出し、修正することができます。

ぜひお試しください!↓↓

注意:AIアシスタントは多くの間違いを犯す

Suggested Prompt: すべての Null References を削除する

Suggested Prompt: すべての Null References を削除する

Without Proper Instructions

With Specific Instructions

ChatGPT

ChatGPT

Claude

Claude

Perplexity

Perplexity

Copilot

Copilot

Gemini

Gemini

DeepSeek

DeepSeek

Meta AI

Meta AI

Grok

Grok

Qwen

Qwen

チャット

チャット

クラウド

クラウド

戸惑い

戸惑い

パイロット

パイロット

双子

双子

ディープ検索

ディープ検索

ターゲット AI

ターゲット AI

グロック

グロック

QWEN

QWEN

結論 ↓

ゼロポインタ例外は、プログラミングにおける最も一般的なランタイムエラーの1つです。


適切な null チェックを実装し、Null Object デザインパターンを使用し、防御プログラミングの実践を採用することで、これらのトラブルのほとんどを削除できます。


検証コードの小さなオーバーヘッドは、アプリケーションの安定性とユーザーエクスペリエンスで大幅に報われる。

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-iii-t7h3zkv

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xliii

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxix

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxvi

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xlii

詳細情報↓↓

ディスカレーター

Code Smells Are Mine(コードの匂いは私のもの)意見.


私はそれを私の10億ドルの間違いと呼びます.それは1965年にゼロ参照の発明でした。

私はそれを私の10億ドルの間違いと呼びます.それは1965年にゼロ参照の発明でした。

トニー・ホア


この記事は CodeSmell シリーズの一部です。


Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks