John R. Rose
1996 年 10 月 4 日
問題:
Java 1.1 では、新しい API が数多く導入されました。その中には、以前の API と置き換わるものもありました。新旧の API が、同じパッケージ内に共存しているものもあります。新しい API が以前の API に置き換わる場合、様々な面でプログラマの作業は困難になりますが、たいていの場合は、それらの API を無視するか使用しないようにすることで対処できます。
部分的な解決策: 以前のクラス、メソッド、およびフィールドが新しいバージョンにより置き換えられた場合、以前のバージョンに「非推奨 (deprecated)」というマークが付けられていれば、プログラマは重要な部分に注意を集中させることができるようになります。同様に、非推奨の API が使用されている場合に、コンパイラがプログラマの注意を喚起することにより、プログラマの負担を軽くすることができます。
詳細:
ドキュメンテーションコメント内で、新しく段落タグ「@deprecated」を定義しました (JLS, $$184 を参照)。このタグは、クラスとメンバドキュメンテーションコメントの両方で表示可能で、特にそのコメントが示す構造に適用されます。
タグ付けされた段落は、空白のままにしておくこともできます。空白にしない場合、その段落では、プログラマが非推奨の機能の使用を避ける方法を説明する必要があります。
「@see」タグの付いた段落は、必ず含める必要があります。同じ機能を持つ新しいバージョンへの参照を示します。
コンパイラに関する限り、ドキュメンテーションコメント行の最初に (空白文字を除く)「@deprecated」という文字列がある場合、対応するクラス、フィールド、メソッド用のクラスファイルに「非推奨」属性を付けます。
さらに、クラスやメンバのアクセス検査ロジックと平行して、コンパイラは、アクセスするクラスやメンバの「非推奨」属性を探し、推奨されないクラスやメンバが使用されている場合には警告を発行します。
非推奨属性は、クラスや個々のメンバに適用されるもので、名前に対して適用されるものではないことに注意してください。1 つのメソッドが、非推奨と「非推奨でない」オーバーロードの両方を持つこともできます。また、「非推奨でない」メンバが非推奨メンバを隠すまたはオーバーライドすることにより、非推奨メンバを削除することもできます。あるメソッドを非推奨にする必要がある場合、そのメソッドのオーバーライドを無効にすることはプログラマの責任です。
非推奨を含むコンパイルユニットが、非推奨クラスまたはメンバを使用するコンパイルユニットとともにコンパイルされる場合、非推奨に関する警告が抑制されてしまいます。このため、警告が発行されることなく、古い API が組み込まれてしまいます。現在のところ、これ以外の理由で非推奨に関する警告が抑制されることはありません。
仮想的な「-strict」オプションをコンパイラに対して指定すると、非推奨に関する警告を含む、すべての警告をハードエラーにすることができます。これは、新しく追加された機能です。
Javadoc では、非推奨の段落を強調表示する必要があります。非推奨の名前への参照の横に、固有の記号文字を付ける必要がある場合もあります。
例題:
もっとも一般的な非推奨メソッドの例を次に示します。
/** * @deprecated * @see #getPreferredSize */ public Dimension preferredSize() { return getPreferredSize(); }(注: 「@deprecated」タグは、空白文字または改行に続けて記述する必要があります。また、「@see」タグは、その行の先頭に置く必要があります。JLS 18 章を参照してください。)
非推奨が単に名前の変更に関するものであれば、「このメソッドは getPreferredSize によって置き換えられました」といった記述は不要です。「@see」タグの付いた段落がユーザに対してその点を示しているからです。
継続して使用されている機能以外の機能により API が再編成される場合、非推奨はより複雑になります。次に示す例では、メソッドの取り消しを行なっています。
/** * Delete multiple items from the list. * * @deprecated Not for public use in the future. * This method is expected to be retained only as a package * private method. * @see #remove(int) * @see #removeAll() */ public synchronized void delItems(int start, int end) { ... }新しい 1.1 API の設計者は、それらが以前の API に取って代わるものかどうかをよく考慮する必要があります。ユーザに対して、以前の API から新しい API への移行を推奨する場合は、新しい API ごとにドキュメンテーションコメントに非推奨を示す段落を追加する必要があります。非推奨を示す段落に何も記述されていない、ということがないように注意してください。何も記述されていないと、非推奨機能を使用したために発生した警告にユーザが適切に対応することができないからです。
ユーザに対して、新しい API への移行を勧める有効な理由は、次のとおりです。
上記の理由は、すべて同等の重要性を持つものではありませんが、どの場合も (使用を禁止しないまでも) 非推奨とする十分な根拠があります。非推奨 API の使用により、デフォルトでハードエラーが発生することはありません。また、いつ新しい API へ移行したらよいかを決定する助けをユーザに与えるため、非推奨の技術的理由をコメントに簡潔に含めることも必要です。
(非推奨 API の段階的廃止の予定日時を具体的に説明するのは適切ではありません。これは、他の方法で伝達すべきビジネス上の決定だからです。)
非推奨クラス内の各メンバについては、プログラマがあるメンバについて特定の情報を提供したいと思うのでなければ、非推奨タグを付ける必要はありません。
ある機能を非推奨とした場合、エンジニアリングを行う組織に対してその情報を伝えるのは良いことです。そうすれば、他の技術者たちも (賛否どちらの場合であっても) その変更に応じて時宜にかなった仕方で対応できます。
その他の設計オプション:
非推奨機能は、コンパイラがドキュメンテーションコメントの内容を認識するようになった最初の事例です。以前は、コンパイラは、ドキュメンテーションコメントが構文木の中で適切な位置にあることを確認していましたが、現在では実装されていません。これまで、コメントの内容に応じてコンパイラを動作させることを軽率に選択することはありませんでした。
コメントの内容を調べる先例としては、「lint」があります。これは /*PRINTFLIKE2*/ などのコメントを使って C の API に注釈を付けていました。
Java のドキュメンテーションコメントは構造化されていること、およびその構造は Java 言語仕様で規定されていることに注意してください。これは、特定の種類のコメント書式はすでに規定されており、Java による設計は、明確に差別化されていない lint の規約よりも適正であることを意味しています。
プラグマを導入し、非推奨の指定に使用する (例: <*deprecated*>) ことも検討されましたが、互換性のない多数の拡張に広く門戸を開く結果になり、深刻な誤用の可能性もあるために却下されました。
ただし、@deprecated はプラグマと同様の機能を持ちます。つまり、@deprecated はソースコード内にあり、その情報を提供しますが、セマンティクスは変更しません。@deprecated が多くのプラグマと異なる点は、人間であるユーザを対象としているという点です。
コンパイラは、警告を発行し、友好的で有用な助言を提供しますが、規則を強制することはありません。非推奨に関する段落の内容を反映するかどうかはプログラマにかかっています。データを主に使用するのは人間であるため、非推奨情報はコメントに含められているのです。
「移行中 (transient)」のようなキーワードを導入することも検討されました。これは、問題に機械的に対処するには役立ちますが、言語自体を変更しなければならなくなります。特に、以前に予約されていなかった識別子 (「var」など) をコードから取り除く必要が生じます。さらに、非推奨の主なユーザは人間であるため、キーワードに依存する方法は非効率的で、コメント規則を追加しなければならなくなります。コメント規則は javadoc に統合するのが望ましい方向です。上記の理由で、キーワードを使用するという方法は現実的でないと判断されました。
従来コンパイラはドキュメンテーションコメントを無視してきましたし、一般に無視すべきです。これには受け入れるべき不規則な例外があります。ただし、非推奨用のその他の設計オプションに含まれる不規則性は望ましいものではありません。