パブリックな識別子、インタフェース、ユーティリティクラス、Java標準ライブラリのパッケージと、同じ名前を再利用してはならない。
たとえばVectorクラスのようなパブリッククラスと同じ名前を持つ識別子を使うと、コードを後でメンテナンスする別のプログラマは、この識別子がjava.util.Vectorクラスのことではないとは知らず、オリジナルのjava.util.Vectorクラスを使うはずが独自のVectorを意図せず使用してしまうかもしれない。独自のVector型が java.util.Vectorのクラス名をシャドーイング(shadow)してしまうことは、Java言語仕様§6.3.2「オブスキュアな宣言」に記述されている通り。[JLS 2005] これはプログラムの意図せぬ動作を引き起こしかねない。
import文を正しく定義すればこの問題を解決できる。しかし、再利用した名前の定義を他のパッケージからインポートする場合は、要求時の型インポート宣言(type-import-on-demand declaration)を使用すること(Java言語仕様§7.5.2 「要求時の型インポート宣言」を参照)。さらによくある失敗は、コードを書いた「後」でインポート文を書いてしまうということである。これはIDEが自動的に行うために発生することも少なくない。こうなると名前の問題はより曖昧になる。Javaのインクルードパスに、本来意図した型より先に独自の型名が見つかると、それ以上検索は行われず、結果として気づかないうちに間違った型が適用されてしまう。
違反コード (クラス名)
以下の違反コード例では、クラス java.util.Vectorと同じ名前を持つクラスを実装している。独自のクラスでは、java.util.VectorのisEmpty()メソッドをオーバーライドすることで、異なる条件に基づいて動作するisEmpty()を導入し、レガシーコードとやりとりしている。しかし、独自のisEmpty()をjava.util.Vector.isEmpty()メソッドと勘違いしてコードを保守すると、プログラムの予期せぬ動作をするかもしれない。
class Vector { private int val = 1; public boolean isEmpty() { if (val == 1) { // 0ではなく1と比較する return true; } else { return false; } } // 他の機能は java.util.Vector と同じ } // import java.util.Vector; 略 public class VectorUser { public static void main(String[] args) { Vector v = new Vector(); if (v.isEmpty()) { System.out.println("Vector is empty"); } } }
適合コード (クラス名)
以下の適合コードでは、クラス名に別の名前を使用しており、Java標準ライブラリのクラスをシャドーイングする可能性を排除している。
class MyVector { //other code }
シャドーイングされる元のクラスも独自に管理しているのであれば、Blochの『Effective Java』に従って元のクラスの設計方針を見直すほうがよいだろう。[Bloch 2008] 元のクラスをインタフェースに変更することで、MyVectorがVectorインタフェースを実装するように宣言することが可能になる。このように変更することで、MyVectorを使用することを意図したクライアントコードは、Vectorの元の実装を使用するコードと互換性を保つことができる。
リスク評価
パブリックな識別子を再利用すると、コードの可読性と保守性が低下する。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
DCL01-J | 低 | 低 | 中 | P2 | L3 |
自動検出
自動化ツールを使うことで、Java標準ライブラリのパブリッククラスやインタフェースの名前の再利用を容易に検出することができる。
関連ガイドライン
The CERT C Secure Coding Standard | PRE04-C. Do not reuse a standard header file name |
The CERT C++ Secure Coding Standard | PRE04-CPP. Do not reuse a standard header file name |
参考文献
[JLS 2005] | §6.3.2, Obscured Declarations |
§6.3.1, Shadowing Declarations | |
§7.5.2, Type-Import-on-Demand Declaration | |
§14.4.3, Shadowing of Names by Local Variables | |
[FindBugs 2008] | |
[Bloch 2005] | Puzzle 67. All strung out |
[Bloch 2008] | Item 16. Prefer interfaces to abstract classes |
翻訳元
これは以下のページを翻訳したものです。
DCL01-J. Do not reuse public identifiers from the Java Standard Library (revision 31)