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

アサーション機能

アサーションとは、boolean 式を含む文で、その文が実行されるときに true であることをプログラマが検証するために使用します。たとえば、データバッファのすべての引数を非整列化した後、プログラマはバッファに残っているデータのバイト数がゼロであると表明します。アサーションを実行するとき、システムは boolean 式を評価して、偽であると評価した場合にエラーを報告します。boolean 式が真であることを確認することによって、システムはプログラムが正しいことを保証し、これによってエラーのないプログラムになる可能性が高くなります。

性能を優先にするときは、アサーションチェックを無効にします。通常、プログラムを開発およびテストするときにはアサーションチェックを有効にしておき、配備するときに無効にします。

アサーションは無効になっている可能性もあるので、プログラムは「アサーションに含まれている boolean 式が評価される」と仮定すべきではありません。したがって、この式により副作用が生じないようにすべきです。つまり、この式を評価することによって何らかの状態に影響を与える (そして、評価の完了後に判明する) ようなことがあってはなりません。アサーションに含まれる boolean 式が副作用を持つことは不正ではありませんが、アサーションが有効または無効であるかどうかによってプログラムの動作が変わる可能性があるので一般的には適切ではありません。

同様に、アサーションは public メソッドの引数チェックに使用しないでください。通常、引数チェックはメソッドの規約の一部であり、アサーションが有効または無効であるかどうかにかかわらず、この規約は遵守される必要があります。アサーションを引数チェックに使用する場合のもう一つの問題は、引数に問題がある場合に適切なランタイム例外 (IllegalArgumentExceptionIndexOutOfBoundsException、または NullPointerException) をスローする必要があるということです。しかし、アサーションが失敗すると適切な例外がスローされません。

詳細については、次の項目を参照してください。

コンパイル

javac バイトコードコンパイラがアサーションを含むコードを受け入れるには、次の例のように、-source 1.4 コマンド行オプションを使用する必要があります。


javac -source 1.4 MyClass.java

構文

新しいキーワードが Java プログラミング言語に追加されました。assert キーワードは、次のように文法が修正された製品と新しい製品で使用できます。


StatementWithoutTrailingSubstatement:
           <All current possibilities, as per JLS,
              Section 14.4> AssertStatement          
AssertStatement:
   assert Expression1;
   assert Expression1 : Expression2; 

どちらの形式の assert 文でも、Expression1 は boolean 型である必要があり、そうでない場合はコンパイル時にエラーが発生します。

セマンティクス

あるクラスでアサーションが無効である場合、そのクラスに含まれる assert 文は何にも影響しません。アサーションが有効である場合、最初の式が評価されます。この式が false であると評価された場合、AssertionError がスローされます。さらに、(最初の式の後にコロンをはさんで) 2 番目の式が存在する場合、この式も評価されて、AssertionError のコンストラクタに渡されます。そうでない場合、パラメータのないコンストラクタが使用されます。最初の式が true であると評価された場合、2 番目の式は評価されません。

どちらかの式の評価中に例外がスローされた場合、assert 文は途中で終了して、当該の例外をスローします。

アサーションの有効化および無効化

デフォルトでは、アサーションは無効になっています。ここで説明する 2 つのコマンド行スイッチを使用すると、アサーションを有効または無効に設定できます。

次のスイッチは、アサーションを様々なレベルで有効にします。


java [ -enableassertions | -ea  ] [:<package name>"..." | :<class name> ]

引数を指定しない場合、アサーションはデフォルトで有効になります。「...」で終了する引数を 1 つ指定した場合、指定されたパッケージおよびそのサブパッケージにおいて、アサーションはデフォルトで有効になります。引数が「...」だけである場合、現在の作業用ディレクトリにある名前のないパッケージにおいて、アサーションは有効になります。「...」で終了しない引数を 1 つ指定した場合、指定されたクラスにおいて、アサーションは有効になります。

同様に、次のスイッチはアサーションを無効にします。


java [ -disableassertions | -da ] [:<package name>"..." | :<class name> ]

これらのスイッチの複数のインスタンスを単一のコマンド行に指定した場合、最初にこれらのスイッチが指定された順番どおりに処理されて、その後で任意のクラスがロードされます。たとえば、プログラムを実行するときに、パッケージ com.wombat.fruitbat (およびそのサブパッケージ) 内だけでアサーションを有効にするには、次のコマンドを使用します。


java -ea:com.wombat.fruitbat...  <Main class>

プログラムを実行するときに、パッケージ com.wombat.fruitbat ではアサーションを有効にするが、クラス com.wombat.fruitbat.Brickbat ではアサーションを無効にするには、次のコマンドを使用します。


java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat <class>

上記スイッチはすべてのクラスローダおよび (クラスローダを持たない) システムクラスに適用されます。この規則には例外が 1 つあります。引数を指定しない場合、このスイッチはシステムクラスには適用されません。 これによって、システムクラスを除くすべてのクラスでアサーションを有効にすることが簡単になります。すべてのシステムクラスでアサーションを有効にする (つまり、システムクラスのデフォルトのアサーション状態を true に設定する) には、次のスイッチを使用します。


 java [ -enablesystemassertions | -esa ]

逆に、すべてのシステムクラスでアサーションを無効にするには、次のスイッチを使用します。


java [ -disablesystemassertions | -dsa ]

プログラムによるアサーションの有効化および無効化

ほとんどのプログラマは次の方法を使用する必要はありません。次の方法は、インタプリタまたは他の実行環境を作成するために提供されています。

クラスローダに対するデフォルトのアサーション状態の設定

各クラスローダは、この後でクラスローダが初期化する新しいクラスにおいて、アサーションをデフォルトで有効または無効にするかを決定する「デフォルトのアサーション状態 (boolean 値)」を持っています。デフォルトでは、新たに作成されるクラスローダのアサーション状態は false (無効) です。次のように新しいメソッドをクラス ClassLoader で呼び出すことによって、この状態はいつでも変更できます。

public void setDefaultAssertionStatus(boolean enabled)

クラスがロードされるとき、そのクラスローダにクラスのパッケージ名またはそのクラス名のアサーション状態に関する特別な命令が (後述する ClassLoader の 2 つの新しいメソッドのどちらかにより) 指定されている場合、このような命令はクラスローダのデフォルトのアサーション状態よりも優先されます。そうでない場合、クラスのアサーションはそのクラスローダのデフォルトのアサーション状態に指定されているとおりに有効または無効になります。

パッケージおよびそのサブパッケージに対するアサーション状態の設定

次の方法を使用すると、パッケージごとにデフォルトのアサーション状態を設定できます。パッケージごとにデフォルトのアサーション状態を設定する場合、実際には、パッケージおよびそのサブパッケージに適用されることに注意してください。

public void setPackageAssertionStatus(String packageName, boolean enabled);

クラスおよびその入れ子クラスに対するアサーション状態の設定

次の方法を使用すると、クラスごとにアサーション状態を設定できます。

public void setClassAssertionStatus(string className, boolean enabled);

クラスローダのデフォルトのアサーション状態へのリセット

次の方法を使用すると、クラスローダに関連するすべてのアサーション状態の設定をクリアできます。

public void clearAssertStatus();

使用上の注意

この節では、アサーションの仕様ではなく、アサーションを使用するときの注意事項について説明します。標準化コミュニティから見れば、この節で説明することは標準ではありません。

この節では、アサーション要素について適切な使用法と不適切な使用法の例を示します。これらの例はすべてを網羅しているわけではなく、アサーションの本来の使用法を理解してもらうことを目的としています。

内部の不変条件

一般的には、プログラムの動作に関する重要な前提を示す短いアサーションを頻繁に使用することが適切です。

アサーション機能が存在しなければ、多くのプログラマは次のようなコメントを使用します。


 if (i%3 == 0) {
        ...
    } else if (i%3 == 1) {
        ...
    } else { // (i%3 == 2)
        ...
    }

このように不変条件を表明している部分がコードに存在する場合、assert に変更するべきです。上記例の場合、assert が if-else 文の else 節を保護するには次のように変更します。


if (i % 3 == 0) {
        ...
    } else if (i%3 == 1) {
        ...
    } else {
        assert i%3 == 2;
        ...
    }

i が負の場合、% 演算子は真の mod 演算子ではないため上記例のアサーションは失敗しますが、残りの計算は続けられ、その結果は負になります。

制御フローの不変条件

アサーションを効果的に使用できるもう一つの例は、デフォルトの case を持たない switch 文です。

たとえば :


 switch(suit) {
      case Suit.CLUBS:
        ...
        break;

      case Suit.DIAMONDS:
        ...
        break;

      case Suit.HEARTS:
        ...
        break;

      case Suit.SPADES:
        ...
    }

プログラマはおそらく、「上記 switch 文の 4 つの case のうちの 1 つが常に実行される」と仮定しています。この仮定をテストするには、次のようなデフォルトの case を追加します。


default:
        assert false;

通常は、プログラマが「本来到達すべき場所ではない」と仮定している場所ならば、次の文はどこにでも置けるはずです。


 assert false;

たとえば、次のようなメソッドがあると仮定します。


 void foo() {
        for (...) {
            if (...)
                return;
         }
         // Execution should never reach this point!!!
    }

最後のコメントを次のように変更します。


assert false;

この技法を使用するときには十分に注意してください。ある文が到達不能である場合 (JLS 14.19 を参照)、その文が到達不能であることを表明しようとすると、コンパイル時にエラーが発生します。

事前条件、事後条件、およびクラスの不変条件

assert 構文は、「規約による設計」を完全に適用していない機能ですが、プログラミングにおける非公式の「規約による設計」スタイルをサポートするのに役立ちます。

事前条件

規約上、公開メソッドの事前条件はメソッド内の明示的なチェックによって実施され、結果として、指定された特別な例外が発生します。次に例を示します。


 /**
     * Sets the refresh rate.
     *
     * @param  rate refresh rate, in frames per second.
     * @throws IllegalArgumentException if rate <= 0 or
     *          rate> MAX_REFRESH_RATE.
     */
     public void setRefreshRate(int rate) {
         // Enforce specified precondition in public method
         if (rate <= 0 || rate> MAX_REFRESH_RATE)
             throw new IllegalArgumentException("Illegal rate: " + rate);

         setRefreshInterval(1000/rate);
     }

assert 構文を追加しても、この規約には影響ありません。アサーションが有効であるかどうかにかかわらず、アサーションが入っているメソッドが必ず引数チェックを実施するような事前条件に対しては、アサーションは不適切です。さらに、assert 構文は指定されたタイプの例外をスローしません。

しかし、private メソッドには事前条件があり、かつ、クラスの作成者が「クライアントがそのクラスで何をしてもその事前条件は保たれる」と考えている場合には、アサーションは適切です。次に例を示します。


  /**
    * Sets the refresh interval (must correspond to a legal frame rate).
    *
    * @param  interval refresh interval in milliseconds.
    */
    private void setRefreshInterval(int interval) {
        // Confirm adherence to precondition in nonpublic method
        assert interval> 0 && interval <= 1000/MAX_REFRESH_RATE;

        ... // Set the refresh interval
    }

MAX_REFRESH_RATE1000 よりも大きく、ユーザが選択したリフレッシュレートが 1000 よりも大きい場合、上記アサーションは失敗するので注意してください。 つまり、これはライブラリのバグを示しています。

事後条件

public メソッドに指定されているかどうかにかかわらず、事後条件チェックはアサーションで実装するのが最適です。次に例を示します。


 /**
     * Returns a BigInteger whose value is (this-1 mod m).
     *
     * @param  m the modulus.
     * @return this-1 mod m.
     * @throws ArithmeticException  m <= 0, or this BigInteger
     *         has no multiplicative inverse mod m (that is, this BigInteger
     *         is not relatively prime to m).
     */
    public BigInteger modInverse(BigInteger m) {
        if (m.signum <= 0)
          throw new ArithmeticException("Modulus not positive: " + m);
        if (!this.gcd(m).equals(ONE))
          throw new ArithmeticException(this + " not invertible mod " + m);

        ... // Do the computation

        assert this.multiply(result).mod(m).equals(ONE);
        return result;
    }

実際には、2 番目の事前条件(this.gcd(m).equals(ONE)) は冗長であるので、計算を実行する前にチェックしません。この事前条件のチェックは、標準アルゴリズムによるモジュラ乗法逆数計算の副作用として行われます。

場合によっては、計算を実行する前にいくつかのデータを保存しておき、計算が完了した後に事後条件をチェックする必要があります。このような事後条件のチェックを行うには、2 つの assert 文と、計算の後にチェック (または再チェック) できるように 1 つまたは複数の変数の状態を保存するように設計された単純な内部クラスを使用します。たとえば、次のようなコードがあると仮定します。


void foo(int[] array) {
        // Manipulate array
        ...

        // At this point, array will contain exactly the ints that it did
        // prior to manipulation, in the same order.
    }

次に、上記メソッドを変更して、形だけのアサーションから機能するアサーションに変更する方法を示します。


 void foo(final int[] array) {

        class DataCopy {
          private int[] arrayCopy;

          DataCopy() { arrayCopy = (int[])(array.clone()); }

          boolean isConsistent() { return Arrays.equals(array, arrayCopy); }
        }

        DataCopy copy = null;

        // Always succeeds; has side effect of saving a copy of array
        assert (copy = new DataCopy()) != null;

        ... // Manipulate array

        assert copy.isConsistent();
     }

このメソッドを簡単に説明すると、複数のデータフィールドを保存して、計算前後の値に関連する複雑なアサーションを任意にテストします。

最初の assert 文 (副作用として単独で実行される) をよりわかりやすくすると、次のようになります。


  copy = new DataCopy();

しかし、アサーションが有効であるかどうかにかかわらず、この文は配列をコピーするので、「無効である場合、アサーションは何にも影響してはならない」という規則に違反します。

クラスの不変条件

すでに述べたとおり、アサーションは内部不変条件をチェックするのに適切です。アサーション機構自体は表明を行うために特別なスタイルを要求しません。ときには、必要な制約をチェックする数多くの式を単一の内部メソッドに結合して、そのメソッドをアサーションで呼び出すようにする方が便利な場合もあります。たとえば、何かのバランスツリーのデータ構造を実装しようとしていると仮定します。この場合はおそらく、ツリーが実際に (データ構造が示すとおりに) 効率的に構築されているかどうかをチェックする private メソッドを実装する方が適切です。


 // Returns true if this tree is properly balanced
    private boolean balanced() {
        ...
    }

このメソッドはクラス不変条件です。どのメソッドにおいても、クラス不変条件は常に (メソッドが完了する前でも後でも) 真である必要があります。これをチェックするには、次のようにアサーションでチェックします。


assert balanced();

各 public メソッドとコンストラクタの直前に、assert 行を置きます。データ構造がネイティブメソッドによって実装されている場合を除いて、一般的に、各 public メソッドの先頭に同様なチェックを置く必要はありません。この場合、メソッドの呼び出し間に、メモリー破壊のバグが「ネイティブピア」のデータ構造を破壊する可能性があります。このようなメソッドの先頭にあるアサーションが失敗した場合、このようなメモリー破壊が発生したことを意味します。同様に、ほかのクラスによって状態が変更される可能性があるクラスにおいては、クラス不変条件のチェックをメソッドの先頭に置くことが望まれます。しかし、クラスの状態はほかのクラスから直接見ることができないように設計することが推奨されています。

クラスファイルからのアサーションのすべてのトレースの削除

リソースが制限されているデバイスを開発しているプログラマは、クラスファイルからアサーションを完全に取り去ってしまいたいと思うかもしれません。こうすることによって、フィールドではアサーションを有効にできなくなりますが、クラスファイルのサイズを減らすことができるので、おそらく、クラスをロードする速度を上げることができます。高品質の JIT が存在しない場合、アサーションのトレースを減らし、ランタイム性能を上げることができます。

アサーション機能は、クラスファイルからアサーションのトレースを削除する機能を直接的にはサポートしていません。しかし、次のように「条件付きコンパイル」(JLS 14.19 を参照) で assert 文を使用することができます。


 static final boolean asserts = ... ; // false to eliminate asserts

     if (asserts)
         assert <expr> ;

この方法でアサーションを使用した場合、コンパイラは自由に、自分が生成するクラスファイルからアサーションのトレースをすべて削除できます。リソースが制限されているデバイスのコードを生成するときには、この方法をできるだけ使用することが推奨されます。

アサーションを有効にするための要件

重要なシステムを扱うプログラマにとっては、フィールドでアサーションが無効にならないほうが望ましいかもしれません。次に、アサーションが無効になっている場合、そのクラスをロードしないようにする例を示します。


  static {
        boolean assertsEnabled = false;
        assert assertsEnabled = true; // Intentional side effect!!!
        if (!assertsEnabled)
            throw new RuntimeException("Asserts must be enabled!!!");
    }

ソース互換性

Java プログラミング言語には assert キーワードが追加されたので、assert を識別子として使用している既存のプログラムは無効になります。しかし、このキーワードが追加されたからと言って、既存のバイナリ (.class ファイル) を使用しても問題は発生しません。assert が正当な識別子であるプログラムから正当な識別子でないプログラムへの移行を簡単にするために、このリリースではコンパイラは 2 つの動作モードをサポートしています。

アサーションを有効にするには、次のコマンド行スイッチを使用します。


 -source 1.4

このフラグを指定しない場合、ソース互換性を最大限にするために、デフォルトの動作は「1.3」です。1.3 とのソース互換性はいずれサポートされなくなる予定です。

設計に関する 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