EXP01-J. オブジェクトが必要な場面で null を使わない
オブジェクトが必要とされるどの場面でも、null 値を使わない。これは以下の例を含む。
nullオブジェクトのインスタンスメソッドを呼び出すnullオブジェクトのフィールドを取得または変更するnullを配列のように扱って長さを取得するnullを配列のように扱って要素を取得または変更するnullをThrowableの値のように扱ってスローする
オブジェクトが必要な場面で null を使うと、NullPointerException がスローされ、プログラムやスレッドの実行が中断される。このコーディングスタンダードに準拠したコードは、「ERR08-J. NullPointerException およびその親クラスの例外をキャッチしない」にしたがって NullPointerException をキャッチしないため、結果として終了する。
違反コード
以下の違反コード例は Reasoning によって最初に発見された Tomcat version 4.1.24 のバグを示している[Reasoning 2003]。cardinality() メソッドは、col コレクションのなかに現れる obj オブジェクトの出現回数を返すように設計されたメソッドである。cardinality() メソッドの正しい使い道の一つは、コレクションのなかにあるオブジェクトのいくつが null であるのかを調べることである。しかし、コレクションに含まれるメンバをチェックするには、式 obj.equals(elt) を使うことになるので、obj が null であり elt が null でない場合に null ポインタ参照が必ず発生する。
public static int cardinality(Object obj, final Collection<?> col) {
int count = 0;
if (col == null) {
return count;
}
Iterator<?> it = col.iterator();
while (it.hasNext()) {
Object elt = it.next();
if ((null == obj && null == elt) || obj.equals(elt)) { // null ポインタ参照
count++;
}
}
return count;
}
適合コード
以下の適合コードでは、明示的なチェックを追加して null ポインタ参照を排除している。
public static int cardinality(Object obj, final Collection col) {
int count = 0;
if (col == null) {
return count;
}
Iterator it = col.iterator();
while (it.hasNext()) {
Object elt = it.next();
if ((null == obj && null == elt) ||
(null != obj && obj.equals(elt))) {
count++;
}
}
return count;
}
違反コード
以下の違反コード例は、指定された String 引数が有効な名前(1つ以上のスペースで区切られた、大文字から始まる2つの単語)なら true を返す、isProperName() メソッドを定義している。
public boolean isProperName(String s) {
String names[] = s.split(" ");
if (names.length != 2) {
return false;
}
return (isCapitalized(names[0]) && isCapitalized(names[1]));
}
isProperName() メソッドは、null を引数として呼び出されると null ポインタ参照を発生させるため、このルールに違反している。
適合コード(ラッパーメソッド)
以下の適合コードでは、isProperName() メソッドの実装は違反コードと同じだが、クラス内の1か所からしか呼び出されない private メソッドになっている。
public class Foo {
private boolean isProperName(String s) {
String names[] = s.split(" ");
if (names.length != 2) {
return false;
}
return (isCapitalized(names[0]) && isCapitalized(names[1]));
}
public boolean testString(String s) {
if (s == null) return false;
else return isProperName(s);
}
}
isProperName() が有効な文字列参照についてのみ呼び出されることを、testString() メソッドが保証している。結果として、isProperName() メソッドが public だと違反になるにもかかわらず、このクラスはこのルールに適合している。このような保証を使うと、null ポインタ参照を排除できる。
適合コード(Optional 型)
以下の適合コードでは、null になりうる String オブジェクトの代わりに Optional String を使っている。Optional クラス(java.util.Optional [API 2014])は Java 8 で導入され、null ポインタ参照を軽減するために使える。
public boolean isProperName(Optional<String> os) {
String names[] = os.orElse("").split(" ");
return (names.length != 2) ? false :
(isCapitalized(names[0]) && isCapitalized(names[1]));
}
Optional クラスのメソッドを使うと、プログラムを短く直感的にできる。[Urma 2014]
例外
EXP01-J-EX0: throws 節かメソッドコメントで NullPointerException をスローする(可能性がある)ことを文書化している場合、有効なオブジェクト参照であることの保証なしにオブジェクト型の引数を参照できる。ただし、この例外は控えめに使うべきである。
リスク評価
null ポインタ参照はサービス運用妨害(denial-of-service)につながる可能性がある。マルチスレッドプログラムにおいては、null ポインタ参照はキャッシュコヒーレンシポリシー(cache coherency policies)に違反し、リソースリークを引き起こす可能性がある。
|
ルール |
深刻度 |
可能性 |
自動検出 |
自動修正 |
優先度 |
レベル |
|---|---|---|---|---|---|---|
|
EXP01-J |
低 |
高 |
不可 |
可 |
P6 |
L2 |
自動検出
null ポインタ参照はプログラムの実行フローに依存して発生する場合がある。ツールによる自動検出には限界があるため、人の手によるコード分析が必要となる場合もあるだろう[Hovemeyer 2007]。メソッド引数に(この引数の値は null ではない、というような意味の)アノテーションをつけることで、検出ツールの処理を助け、人の手によるコード分析の必要性を減らすことができる。このようなアノテーションを行うことは強く推奨される。
| ツール | バージョン | チェッカー | 説明 |
|---|---|---|---|
| The Checker Framework | 2.1.3 |
Nullness Checker |
Null pointer errors (see Chapter 3) |
| CodeSonar |
9.0p0 |
JAVA.DEEPNULL.PARAM.EACTUAL JAVA.DEEPNULL.EFIELD JAVA.DEEPNULL.FIELD JAVA.NULL.PARAM.ACTUAL JAVA.NULL.DEREF JAVA.DEEPNULL.DEREF JAVA.DEEPNULL.RET.EMETH JAVA.DEEPNULL.RET.METH JAVA.NULL.RET.ARRAY JAVA.NULL.RET.BOOL JAVA.NULL.RET.OPT JAVA.STRUCT.UPD JAVA.STRUCT.DUPD JAVA.STRUCT.UPED JAVA.DEEPNULL.PARAM.ACTUAL |
Actual Parameter Element may be null Field Element may be null (deep) Field may be null (deep) Null Parameter Dereference Null Pointer Dereference Null Pointer Dereference (deep) Return Value may Contain null Element Return Value may be null Return null Array Return null Boolean Return null Optional Unchecked Parameter Dereference Unchecked Parameter Dereference (deep) Unchecked Parameter Element Dereference (deep) null Passed to Method (deep) |
| Coverity |
v7.5
|
FORWARD_NULL |
Implemented |
| Fortify | V. 5.0 |
Missing_Check_against_Null |
Implemented |
| Findbugs | V. 2.0 |
NP_DEREFERENCE_OF_READLINE_VALUE |
Implemented |
| Klocwork |
2025.2 |
NPE.COND NPE.CONST NPE.RET NPE.RET.UTIL NPE.STAT REDUN.EQNULL |
|
| Parasoft Jtest | 2024.2 | CERT.EXP01.NP CERT.EXP01.NCMD |
Avoid NullPointerException Ensure that dereferenced variables match variables which were previously checked for "null" |
| PVS-Studio |
7.38 |
V6008, V6073, V6093 |
|
| SonarQube | 9.9 | Null pointers should not be dereferenced "toString()" and "clone()" methods should not return null Null should not be returned from a "Boolean" method "@NonNull" values should not be set to null |
|
| SpotBugs |
4.6.0 |
NP_DEREFERENCE_OF_READLINE_VALUE NP_IMMEDIATE_DEREFERENCE_OF_READLINE NP_ALWAYS_NULL NP_NULL_ON_SOME_PATH NP_NULL_ON_SOME_PATH_EXCEPTION NP_NULL_PARAM_DEREF NP_NULL_PARAM_DEREF_NONVIRTUAL NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS NP_TOSTRING_COULD_RETURN_NULL |
Implemented |
関連する脆弱性
JDK 1.6 の update 4 より前のバージョンにおける Java Web Start アプリケーションとアプレットには、セキュリティ上重大な問題を引き起こすバグが存在した。場合によっては、アプリケーションやアプレットがサーバとの間で HTTPS 接続を確立しようとすることで NullPointerException が発生した[SDN 2008]。HTTPS によるセキュアな接続を確立できなかった結果、サービス運用妨害(denial-of-service)が発生した。クライアントはセキュアでない HTTP 接続による通信を一時的に行わざるを得なくなったのである。
関連ガイドライン
| ISO/IEC TR 24772:2010 |
Null Pointer Dereference [XYH] |
|
CWE-476, NULL Pointer Dereference |
実装の詳細 (Android)
Android アプリは、携帯端末のメモリが限られているため NullPointerException に対してより敏感である。static メンバや Activity のメンバは、メモリが不足すると null になる可能性がある。
参考文献
|
[API 2006] |
|
| [API 2014] | Class java.util.Optional |
|
|
|
|
"Defect ID 00-0001" |
|
|
[SDN 2008] |
|
| [ Seacord 2015 ] | |
| [Urma 2014] | Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional! |
翻訳元
これは以下のページを翻訳したものです。
EXP01-J. Do not use a null in a case where an object is required (revision 167)
