Java 2 SDK 開発ガイド (Solaris 編)

設計に関する FAQ

次に、アサーション機能の設計に関するさまざまな FAQ を示します。

設計に関する FAQ - 一般的な質問

  1. 特別なサポートなしで Java プログラミング言語上にアサーションをプログラムできるのにアサーション機能を提供するのはなぜでしょうか。

    アドホックな実装も可能ですが、アサーション機能は必然的に見た目が悪く(アサーションごとに if 文が必要)、非効率的です (アサーションが無効の場合でも状態を評価する)。さらに、アドホックな実装はそれぞれ独自のアサーションを有効または無効にする手段を持つことになるので、特に、フィールドでのデバッグにおいてこのような実装の利点を失ってしまいます。これらの欠点のために、アサーションは Java の機能として取り入れられませんでした。アサーションのサポートがプラットフォームに追加されれば、この状況は改善されるでしょう。

  2. アサーションの実装にライブラリではなく言語の変更が採用されたのはなぜでしょうか。

    言語の変更は簡単ではなく、たいへんな努力が必要です。ライブラリによるアプローチも考えました。しかし、アサーションを無効にした場合の実行時コストがごくわずかになることが重要であると判断しました。これをライブラリで行おうとすると、プログラマは各アサーションを if 文としてハードコードする必要があります。ほとんどのプログラマはこのような作業を行いたくはありません。その場合 if 文を省略して性能を落とすか、またはアサーション機能を完全に無視します。実は、James Gosling による Java の最初の仕様にはアサーション機能が含まれていました。しかし、時間の制約のために十分な設計および実装ができなかったため、Oak 仕様から削除されました。

  3. Eiffel プログラミング言語のように、事前条件、事後条件、およびクラス不変条件に、「規約による設計」機能を本格的に提供しなかったのはなぜでしょうか。

    このような機能を提供することも考えましたが、Java プラットフォームライブラリを大幅に変更せずに、しかも、古いライブラリと新しいライブラリ間の不整合を最小限に抑えながら、このような機能を Java プログラミング言語に実装できるかどうかに確信が持てませんでした。さらに、このような機能が Java の最大の特徴である簡易性を損なわないかどうかにも確信が持てませんでした。すべてを考慮して、単純な boolean 型のアサーション機能がもっとも簡単なソリューションであり、もっともリスクが少ないと結論付けました。ただし、boolean 型のアサーション機能を言語に追加したと言っても、将来、「規約による設計」機能を本格的に追加する可能性を否定するわけではありません。

    単純なアサーション機能は、制限はあるものの、「規約による設計」スタイルのプログラミングを実現します。assert 文は事後条件とクラス不変条件のチェックに適切です。事前条件のチェックは依然、指定された特別な例外 (IllegalArgumentExceptionIllegalStateException など) を生成するメソッド内で行う必要があります。

  4. boolean 型のアサーションとは別に、アサーションが無効になっている場合にコード全体の実行を抑制するような (アサーションに似た) 機能を提供しなかったのはなぜでしょうか。

    このような機能を提供すると、プログラマは、別のメソッドを使用した方がいい場合でも、複雑なアサーションをインライン化してしまう可能性があるためです。

設計に関する FAQ - 互換性

  1. 新しいキーワードによって、assert を識別子として使用している既存のプログラムに互換性の問題は発生しませんか。

    ソースファイルの場合は発生します。しかし、assert を識別子として使用しているクラスのバイナリはそのまま動作します。移行を簡単にするために、どのようにすれば開発者は移行期間中に assert を識別子として使用し続けることができるかを説明します。ソース互換性を参照してください。

設計に関する FAQ - 構文およびセマンティクス

  1. Expression 2 のプリミティブ型を使用できるのはなぜでしょうか。

    このタイプの式を制限する理由はありません。任意のタイプを許可すると、たとえば、アサーションごとに一意の整数コードを関連付けたい開発者には便利になります。さらに、この式をさらに望ましい System.out.print(...), という形にすることが可能になります。

設計に関する FAQ - AssertionError クラス

  1. Expression2 が存在しない assert 文によって AssertionError が生成される場合、表明された状態のプログラムテキストが詳細なメッセージとして使用されない (たとえば、「height < maxHeight」) のはなぜでしょうか。

    こうすることによって、アサーションの「既成概念にとらわれない」便利さを向上させる場合もありますが、このような文字列定数すべてを .class ファイルと実行時イメージに追加するということで発生するコストに見合うほどの利点ではありません。

  2. AssertionError が発生すると、このエラーを生成したオブジェクトにアクセスできなくなるのはなぜでしょうか。同様に、詳細なメッセージの代わりに、任意のオブジェクトをアサーションから AssertionError に渡さないのはなぜでしょうか。

    このようなオブジェクトへのアクセスを許可すると、プログラマがアサーションの失敗からの復帰を試みようとするため、アサーション機能の目的から逸脱します。

  3. コンテキストアクセス用メソッド (getFile、getLine、getMethod など) を AssertionError で提供しないのはなぜでしょうか。

    この機能は Throwable で提供するのが最良であるので、AssertionError だけではなく、すべての Throwable で使用できます。アサーション機能が最初に登場するリリースまでには、Throwable を拡張して、この機能を提供できるようにする予定です。

  4. AssertionError が RuntimeException ではなく Error のサブクラスなのはなぜでしょうか。

    この問題は大いに議論されました。専門家グループが長時間議論した結果、プログラマがアサーションの失敗を復帰しないようにするには、Error がより適切であるという結論に達しました。一般的には、アサーションが失敗した原因を突き止めることは困難または不可能です。このような失敗は、プログラムが「未知の領域」で動作しており、実行を継続しようとすると危険であることを示しています。さらに、規約によると、メソッドはスローする可能性があるほとんどのランタイム例外を (「@throws」doc コメントにより) 指定することになっています。アサーションの失敗を生成するような条件をメソッドで指定するのは意味がありません。このような情報は実装の詳細である (つまり、実装やリリースごとに変更できる) と考えることができます。

設計 FAQ - アサーションの有効化および無効化

  1. オブジェクトファイルからアサーションを完全に削除するコンパイラフラグを提供しないのはなぜでしょうか。

    「フィールドでアサーションを有効にできるようにする」という強い要求があります。開発者がコンパイル時にオブジェクトファイルからアサーションを削除できるようにするという可能性もありました。しかし、アサーションには本来はあってはいけない副作用が生じることもあるため、このようなフラグはプログラムの動作を大幅に変えてしまう可能性があります。有効な Java プログラムごとにセマンティクスが 1 つだけ関連付けられていることが理想的です。また、オブジェクトファイルにアサーションを残しておけばフィールドで有効にできるので、ユーザにはこちらの方が推奨されます。最後に、標準 Java の「条件付きコンパイル」(JLS 14.19 を参照) を使用すると、必要に応じてこの結果を実現することができます。

  2. setPackageAssertionStatus のセマンティクスが、単純なパッケージ型でなく、パッケージツリー型なのはなぜでしょうか。

    実際には、プログラマはパッケージ階層を使用して自分たちのコードを編成しているので、階層型の制御の方が便利です。たとえば、パッケージツリー型のセマンティクスを使用すると、一度にすべての Swing でアサーションを有効または無効にできます。

  3. 呼び出されたときにはすでにアサーション状態を設定するには遅すぎた場合 (つまり、名前付きクラスがすでにロードされている場合)、setClassAssertionStatus が例外をスローするのではなく、boolean 値を戻すのはなぜでしょうか。

    アサーション状態を設定するには遅すぎた場合、警告メッセージなど以外は対処の必要がないか、対処しないでください。 例外のスローは適切ではありません。

  4. setDefaultAssertionStatus と setAssertionStatus の代わりに単一メソッドをオーバーロードしないのはなぜでしょうか。

    メソッドの名前付けにおいては、わかりやすさを優先するためです。

  5. アプレットがアサーションを有効または無効にすることを防ぐための RuntimePermission が存在しないのはなぜでしょうか。

    アプレットは任意の ClassLoader メソッドを呼び出してアサーション状態を変更する必要はありませんが、これを許可するとマイナスの状況が生じる可能性があります。まだロードされていないクラスのアサーションを有効にすると、最悪の場合、アプレットは弱い DoS 攻撃を受ける可能性があります。さらに、アプレットがアサーション状態を変更できるのは、アプレットがアクセスできるクラスローダによってロードされる予定のクラスだけです。すでに、信頼できないコードがクラスローダへのアクセス権を取得できないようにするための RuntimePermission は存在します (getClassLoader)。

  6. 包含クラスのアサーション状態を照会するための構文を提供しないのはなぜでしょうか。

    このような構文を提供すると、プログラマは複雑なアサーションコードをインラインにしようとし、これは望ましくありません。


               if (assertsEnabled()) {
                   ...
               }

    さらに、必要であれば、現在の API の上のアサーションの状態を照会することは簡単です。


     boolean assertsEnabled = false;
              assert assertsEnabled = true;  // Intentional side-effect!!!
              // Now assertsEnabled is set to the correct value