kagamihogeの日記

kagamihogeの日記です。

JEP 277: Enhanced Deprecationをテキトーに訳した

http://openjdk.java.net/jeps/277 をテキトーに訳した。

JEP 277: Enhanced Deprecation

Owner    Stuart Marks
Created 2014/11/20 23:58
Updated 2015/12/02 04:05
Type    Feature
Status  Candidate
Component   core-libs
Scope   SE
Discussion  jdk9 dash dev at openjdk dot java dot net
Effort  M
Duration    M
Priority    2
Reviewed by Mark Reinhold
Release 9
Issue   8065614

Summary

非推奨アノテーションを改良し、対象機能ライフサイクル終了を強化するツールを提供します。

Goals

  • 仕様におけるAPIの位置付けとステータスに関するより良い情報を提供します。
  • アプリケーションにおける非推奨APIの動的な使用はログが取れるため、ランタイムが警告を出すように拡張します。
  • アプリケーションにおける非推奨APIの使用状況に関する静的な情報を生成するツールを提供します。
  • Java SEとJDKの機能のライフサイクルに関するポリシーを明確にします。特に、非推奨アノテーションが付けられた機能に関する要求事項と、機能削除前に与える必要のある注意事項について、が対象です。既に存在しない機能については状況に応じた方法で削除が可能です*1

Non-Goals

本JEPではjava.lang.Deprecatedアノテーションjavadoc@deprecatedタグの統合は行いません。

Motivation

Java SEの非推奨化メカニズムの改善が必要とされています。一貫性が無くなっており、意味付けと適切な使用に関して混乱が生じています。

非推奨メカニズムはJDK 1.1でjavadocタグとして導入されました。@DeprecatedアノテーションJava SE 5で導入されました。@DeprecatedアノテーションAPI仕様は以下の通りです。

@Deprecatedアノテーションを付与するプログラム要素は、その使用をプログラマ
が推奨しないもので、一般的な理由としては危険であるとか、より良い代替案が
存在する、などです。非推奨なプログラム要素が使われていたり、非推奨では
ないコードでオーバーライドされていると、コンパイラは警告を出します。

Java言語仕様のsection 9.6.4.6はこれと良く似ています。一つ目の文は上述のAPI仕様のものと同一です。言語仕様ではコンパイラが警告を発するために必要となる正確で詳細な状況について記しています。

非推奨メカニズムの意図はAPIライフサイクルの終了について周知するためのものです。アプリケーションの移行を推奨したり、アプリケーションが非推奨APIに新たな依存関係を作ってしまわないようにします。この事は仕様に明示されてはいませんが、各種のドキュメントにおいて非推奨APIは何らかのタイミングで削除されるもの、と記しています。

@Deprecatedアノテーション複数の異なる目的に使われるようになってしまっています。極少数の非推奨APIは実際に削除されたものの、非推奨APIが削除されることは無いだろうと思い込むようになっています。一方、非推奨APIは実際に削除されると考えている人たちも存在し、どちらも意図とは異なる結果になっています。この状況は開発者に@Deprecatedの意味について不明瞭なメッセージを届ける結果になっており、どちらかというと、非推奨APIに遭遇した場合は開発者が何とかしないといけないもの、となっています。誰もが非推奨の実際の意味するところに困惑していますが、誰も真剣には取り合っていませんでした。このため、Java SEから何らかの機能を削除することは既に困難となっています。

非推奨に関するそれ以外の問題としてはコンパイル時にしか警告を出さないことです。JDKの後続バージョンであるAPIが非推奨になるとして、既存のバイナリが非推奨APIに依存したり使用していも警告は出ません。非推奨APIJDKリリースで削除されるとして、その非推奨化のリリースが行われると、旧アプリケーションバイナリの使用者には突然の死が訪れることでしょう。そのアプリケーションはある日突然リンクエラーで失敗し、特にこれといった警告は出ません。なお悪いことに、開発者には既存バイナリが非推奨APIの依存性を持つかどうかチェックする術がありません。このため、バイナリ互換性と旧APIの削除を伴う仕様拡張の必要性との間に、強い緊張関係が生じています。

Description

Specification: Definitions

@Deprecatedアノテーションを機能強化する主要な目的は、ランタイムおよびツールに対して、APIの非推奨化状態に関するより詳細な情報を提供することです。情報は最小かつ洗練されたものにします。

java.lang.Deprecatedアノテーション型に以下のメンバを追加します。

  • Reason eunm型。enum定数値とその定義は後述します。
  • Reason[]を返すvalue()メソッド。デフォルト値はenum定数UNSPECIFIEDです。enum定数の一つ以上の組み合わせを取ることが可能です。ただし、あらゆる組み合わせが有効というわけではありません。空の配列は、直感的ではないものの、構文上は可能です。その場合にはUNSPECIFIEDと同様に扱うべきです。
  • String[]を返すreplacement()メソッド。このメソッドが返す文字列は非推奨APIを置き換えるAPIへリンクです。文字列のフォーマットは@link javadocタグと同じです。
  • Stringを返すsince()メソッド。この文字列にはAPIが非推奨となったリリースナンバーが含まれます。シンタックス自由形式ですが、リリースのナンバリングは@since javadocタグと同じスキーマに従います。この値はjavadoc@sinceタグと重複してはいません@sinceタグはAPIが導入されたリリースを記録するものですが、@DeprecatedアノテーションsinceメソッドAPIが非推奨化されたリリースを記録するものです。

Specification: Enumeration constants

  • UNSPECIFIED. 明示的な理由の無い非推奨化されたAPI。デフォルト値。現在非推奨なものの非推奨化理由は暗黙的にUNSPECIFIEDとなります。
  • CONDEMNED. JDKの将来のリリースで削除予定API。なお、ここで"condemned"という単語を使っているのは削除の意図を示すためです。道徳的な非難(moral censure)の意図はありません。*2
  • DANGEROUS. このAPIを使用すると、データロス・デッドロック・セキュリティ脆弱性・不正確な結果・JVM統合性の喪失(loss of JVM integrity)、となる恐れがあります。
  • OBSOLETE. このAPIは既に不要であり、使用箇所は削除すべきです。代替APIは存在しません。注意点として、OBSOLETEのAPIはCONDEMNEDなこともあればそうで無い場合もあります。
  • SUPERSEDED. このAPIは新しいAPIで置き換えられており、そちらに移行すべきものです。注意点として、SUPERSEDED APIはCONDEMNEDなこともあればそうで無い場合もあります。
  • UNIMPLEMENTED. このAPI呼び出しは何も起きないか、無条件に例外をスローします。
  • EXPERIMENTAL. このAPIは仕様における不安定な部分であり、予告無く互換性の無い変更が行われたり削除されたりする場合があります。

Specification: Updating annotation usage

JDK内部の@Deprecated使用箇所を調査し、場合に応じてアノテーション削除やReason値の更新を行います。以下はその例です。

  • @Deprecated(SUPERSEDED)を、Hashtable, Stack, Dictionary, Vector, Timerに追加。
  • @Deprecated(OBSOLETE)を、Process()引数無しコンストラクタに追加。
  • Thread.destroy()とThread.stop(Throwable)に@Deprecated({UNIMPLEMENTED, CONDEMNED})を付与するよう修正。
  • Thread.suspend(), Thread.resume()とThread.stop()の引数無しオーバーロードに@Deprecated(DANGEROUS)を付与するよう修正。
  • java.awt.Component.show()とhide()から@Deprecatedを削除。

Implementation: Runtime changes

JREは非推奨APIの使用に関する警告を出す機能を提供すべきです。例えば、非推奨クラスロード時に警告を発する"-verbose:deprecation"などのコマンドラインオプション追加が考えられます。非推奨メソッド呼び出し時に警告を出すなど、より詳細なオプションも検討しています。非推奨の警告設定を実行時のライブラリコードからもアクセス可能にすることで、非推奨なAPI機能が使われた場合に警告メッセージを出せるようになります。警告メッセージは"grep"などで検索出来るようにその内容を明確に示すキーワードにすべきです。"deprecated"などの単語では不十分です。非推奨警告を特定のファイルに結びつける方法(JVMで可能であれば)の提供も検討に値するかもしれません。

警告を出すreason値を選択する手段を実行時オプションで提供するでしょう。

実行時に警告オプションを指定しない場合、非推奨APIと機能の使用は特に問題無く許容され、重大なパフォーマンスインパクトが発生することも無いと思われます。

Implementation: Dependency-tool enhancements

jarファイル(やその他のクラスファイルのコンテナ)で非推奨クラスやメンバの使用状況を調査する静的解析ツールを開発者が提供可能になります。jdeptsツールの機能と似ており、このツールを拡張あるいは修正することになると思われます。

Implementation: Javadoc enhancements

javadocツール@Deprecatedアノテーションのreasonコードを扱うための機能拡張が必要です。また、Reason値を強調表示することも必要です。`@deprecated javadocタグの処理は大きくは変わりませんが、Reason値に関する情報を持つための修正が恐らくは必要と思われます。

必要な場合には、標準doclet 非推奨APIを完全に分離するような修正も可能となっています。例えば、クラスの非推奨メンバを別のタブに、既存のタブであるインスタンス・abstract・メソッドと共に並べます。非推奨クラスはpackageフレーム内の異なるセクションに分離します。現状では、パッケージセクションには、インタフェース・クラス・Enum・例外・Errorが含まれます。ここに非推奨メンバのセクションを追加することも可能です。

非推奨APIのリストも同様に拡張が可能となっています。(このページは各ページの最上段にある、概要・パッケージ・クラス・使用・階層ツリー・非推奨・索引・ヘルプ、のバーのリンクから辿れます。)現在、非推奨APIのページは以下の要素で構成されており、インタフェース・クラス・例外・アノテーション型・フィールド・メソッドコンストラクタアノテーション型の要素、です。Condemnedな要素は、削除予定という最重要なものなので、最も目立つ位置に配置されるでしょう。

Implementation: Other tools

非推奨アノテーションに対する機能拡張はIDEと静的解析システムなどのツールに影響を与えます。その種のツール修正は本JEPの範囲を越えていますが、このJEPではそれらのツール修正に関する推奨事項を提供します。例えば、非推奨APIはデフォルトではIDEのオートコンプリートのメニューとダイアログから隠すべきです。また、自動リファクタリングルールで非推奨API呼び出しをその代替APIへと置き換えが可能です。

Alternatives

代替案としては、非推奨機能の使用を検出した場合、実行停止やJVMを停止したりします。これにより非推奨機能の移行の重要性を強制するのに有用に見えますが、実際のところ、非推奨機能の使用に大変な苦痛を伴うこととなり、また、開発者と管理者に必要な情報を何も提供しません。非推奨機能使用時の停止は、その機能についてだけ示すに過ぎません。そうした非推奨機能を検出するには、実行完了後に分析可能な警告を有効にした状態で、システムを通常通り実行可能である必要があります。そうすることで、アプリケーションにおける非推奨機能の使用状況に関する完全な状況が得られます。

非推奨機能をデフォルトでは無効化し、バージョン指定フラグがある場合のみ有効化することも可能です。これは有望な案に見えますが、望む効果が得られそうにありません。JDKのメジャーリリースアップグレード時に"-XenableJava10DeprecatedFeatures"などのフラグ指定が必須である、とただ単に管理者は考えることでしょう。これでは非推奨機能の実際の使用状況や扱い方に関する情報を何も提供していません。

@Deprecatedは残して@deprecated javadocタグを廃止する案もあります。これらは狭義には重複しており、@deprecated Javadocタグと@Deprecatedアノテーションは常に両方あるかどちらも無いか、にすべきです。片方が無いと意味を成しません*3。しかし、タグとアノテーションはそれぞれ異なる利用者に異なる情報を提供しています。@deprecated Javadocタグはドキュメントに含む記述的なテキスト(descriptive text)です。@Deprecatedアノテーションツールおよびランタイムに詳細な情報を提供します。

ドキュメントの文字列をタグからアノテーションに移行するとなると、ドキュメント文字列のアノテーション値がランタイムイメージ内に散乱することになり、また、Javadocは現存のドキュメントコメントではなくアノテーションから抽出するような改修が必要となります。

Testing

TBD

Risks and Assumptions

TBD

Dependences

TBD

Open Issues

  • オプションをjavacに与えない限りコンパイル時には非推奨APIを不可視化する件について。-Xlint:deprecation -Werrorと比較して有用かどうか不明。
  • Revisit the version-specific compiler option idea from the requirements document. Note, this would need to have some kind of version information in the annotation itself.
  • 削除に対するより厳格なプロセスの定義。どのような通知が必要か? 期間は? または、メジャーリリース番号を指定するのか? アップデートで削除が可能なのか?
  • non-goalの記述とは逆に、Javadocタグとアノテーションの調整が含まれている、あるいは、少なくともイシューにその件の説明が含まれている。
  • CONDEMNEDは理由ではなく性質である(CONDEMNED is really a disposition, not a reason.)。reason値とは異なるものに分けるべきでは?
  • @Deprecationアノテーションにテキスト(説明?(description?))値の追加。このテキストはJavadoc @deprecatedタグのテキストを置換するのか?
  • アノテーションjavadocを設置可能なAPIを持たない非推奨機能はどうするべきか? そうしたAPIではない機能には過去に非推奨と宣言されたものが既に存在します。例えば、apt(削除済み), RMI static stubs, RMI HTTP, CGI proxying, セキュアハッシュ, 暗号化アルゴリズム、など。これらは一覧にしたり、追跡の方法が必要な可能性があります。
  • EXPERIMENTALは、メジャーリリースでだけ互換性が変更可能なAPIなのか、常に変更可能なのかを、区別すべきなのか?
  • EXPERIMENTALはAPIライフサイクルの終了とは無関係に見えるので少し違うのでは。基本的には新規APIにつけるもので、古いものに適用するものではない。However, an experimental API might be introduced at some point, and quickly withdrawn in favor of the real one (or nothing at all). 古いものを削除してから新しいものを導入することと、APIの変更とは、基本的には等しいです。よって、試験的なAPIとは潜在的に短いライフサイクルであり、試験的なAPIが導入される場合、すぐに削除されるものと考えられます。
  • OBSOLETEとSUPERSEDEDの両方とも必要かどうか不透明。
  • プロパティで機能の非推奨化を制御する方法について考慮する必要がある。例えば、java.util.Arrays.useLegacyMergeSortを非推奨化する方法とは? おそらく、実行時に警告フラグが設定される場合はライブラリチェックが必要となる。
  • メソッドコンストラクタの非推奨化には、a) クライアントからの呼び出し・b) サブクラスからの呼び出し・c) サブクラスオーバーライド、を区別する必要があると思われます。また、クラスはサブクラス化の完全な非推奨化を考慮する必要があります(ファイナライズに関する問題など)。これらのうち幾つかはメソッドコンストラクタを非推奨化によるモデル化が可能で、例えば、メソッド移行時にはfinalにする、などです。ただし、publicコンストラクタを持つクラスは、クラスをfinalにしない限りオーバーライドは抑制出来ないため、このやり方は限定的になる場合がありえます。これに関してScala には@deprecatedInheritanceと@deprecatedOverridingがあります。https://issues.scala-lang.org/browse/SI-6162 と関連リンク、特にプルリクエストのディスカッション https://github.com/scala/scala/pull/1026https://github.com/scala/scala/pull/1284 を参照してください。
  • ステータスを追跡する外部アーティファクトへのリンクを提供する必要がJavadocなどで必要となるかもしれない。バグレポートで可能か?
  • See also http://mreinhold.org/blog/removing-features and JSR 270. Note, actual JSR 270 policy states that features aren't actually deleted but instead are made optional (but they might not be present in every implementation). TCK still has them but of course enabled optionally. Also mentions of co-bundled jars and endorsed standards mechanism. Need to reconcile that with current modularity work.*4
  • Assess impact on JLS 9.6.4.6.

*1:This should allow defunct features to be removed in a more timely fashion.が原文。

*2:condemnedは、死刑囚とか社会的に存在が許されないこととか、そういうネガティブな意味合いを持つ。で、これには将来的には無くすべきもの、という意味合いが含まれている。なので、このAPIは将来的に無くすべきもの、を明示するためにcondemnedという単語を使っている、のだと思われる。よって、moral censureの意図は無いという記述には、condemnedには削除予定以上の意味は無いですよ、と言っているのだと思われる。

*3:it never makes sense to have one without the other.が原文。これの一つ前の文で、タグとアノテーション両方付けるもの、って書いてるんで、この文は片方だけじゃ意味が無い、と言っているのだと思うが……ちょっと訳に自信が無い

*4:訳すのに力尽きた