14

「クラスとインターフェイスの初期化」を学んでいますが、「T は最上位クラスであり、T 内にレキシカルにネストされた assert ステートメントが実行されます」と表示されます。「T はトップレベル クラスであり、T 内にレキシカルにネストされた assert ステートメントが実行される」とはどういうことか教えてください。例によってですか?

この文はJLSからのもので、原文は次のようなものです。

クラスまたはインターフェイスの型 T は、次のいずれかが最初に発生する直前に初期化されます。

  • T はクラスであり、T のインスタンスが作成されます。
  • T はクラスであり、T によって宣言された静的メソッドが呼び出されます。
  • T によって宣言された static フィールドが割り当てられます。
  • T によって宣言された静的フィールドが使用され、そのフィールドは定数変数ではありません (§4.12.4)。
  • T は最上位クラスであり、T 内でレキシカルにネストされた assert ステートメント (§14.10) が実行されます。
4

4 に答える 4

5

私はそれについて部分的な説明をすることができます。アサーションの有効化/無効化を指します。アサーションは、-eavm 引数によって有効になります。

重要な点assertは次のとおりです。

クラスの初期化が完了する前に実行された assert ステートメントは有効です。

が指定されておら-eaず、次のコードを実行するとします。

 public class Q1 {
    public static void main(String[] args) {
        Bar b = new Bar();
    }
}
class Bar {
    static {
        boolean enabled = false;
        assert  enabled = false; //line(a)
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
        System.out.println("as");
        Baz.testAsserts();
    }
}
class Baz extends Bar {
    static void testAsserts() {
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
    }
}

が初期化される上記の例でbは、Java は before が呼び出されることを保証しline(a)、アサーションは無効になります (つまり、line(a) はまったく実行されません)。assert enable/disable はクラスの初期化の一部であるため、問題のステートメントに記載されています。

トップレベルのクラスが言及され、他のすべてのクラスが this であるとは限らない理由。ここでのより詳細な動作:

public class Q1 {
    public static void main(String[] args) {
        Baz.testAsserts(); 
        // Will execute after Baz is initialized.
    }
}
class Bar {
    static {
        Baz.testAsserts();
        // Will execute before Baz is initialized!
    }
}
class Baz extends Bar {
    static void testAsserts() {
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
    }
}

フラグも-ea使用されていませんが、それでも . がスローされAssertionExceptionます。何が起こるかは次のとおりです。

  1. Q1.main が呼び出されます
  2. Q1.main は Baz.testAsserts を呼び出します。
  3. Baz は Bar を拡張し、Bar は初期化されていないため、JLS に従って Bar を初期化しようとします。
  4. Bar の静的ブロックが呼び出されます。assert ステートメントは、そのクラスの初期化が完了する前、または assert が呼び出される前 (いずれか早い方) に有効になることに注意してください。この場合、まだ完全に初期化されていないためtrue、この段階にありますBar
  5. Bar呼び出しの静的Baz.testAsserts()。アサートはまだ有効です (アサーションの無効化はクラスの初期化と関係があり、Bar はまだ完全に初期化されていないことに注意してください)。Baz.testAsserts() が をスローするようになりAssertionExceptionました。

上は抜け穴です。assertJLS は、最上位クラスで実行する前に、それを無効/有効にすることのみを保証します (どのような vm 引数が与えられても)。ただし、最上位クラスでない場合、動作は最上位クラスの初期化に依存します。これを説明するには、次を参照してください。

class Bar {
    static {
        //Baz.testAsserts();
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
        // Will execute before Baz is initialized!
    }
}
class Baz extends Bar {
    static void testAsserts() {
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
    }
}

これは適切に初期化されたものとして出力Asserts disabled Asserts disabledされBarます。クラスのBar初期化が無効になり、したがって が無効になります。assertBaz

于 2013-07-23T07:51:58.640 に答える
2

この仕様の読み方はわかっていますが、OpenJDK 1.7.0_40 は示されているとおりに動作せず、Oracle JDK 1.7.0_25 も動作しません。

トップレベルクラスは、他のクラスにネストされていないクラスです。アサーション ステートメントは、実行可能コード、つまりメソッド、コンストラクタ、または静的初期化ブロックで発生する可能性があります。これらのケースのほとんどは、他のアイテムによって処理されます。静的メソッドは既にカバーされています。他のメソッドとコンストラクターは、そのクラスのオブジェクトの作成に該当し、静的初期化ブロックは、任意の結果である初期化プロセスの一部です。他のイベントの。

したがって、これらのケースをトリガーせずにレキシカルにネストされたステートメントを持つ唯一の方法は、ネストされたクラスを使用することです。たとえば、次のようなものです。

class Outer {
    static {
        System.out.println("Outer initialized");
    }
    static class Nested {
        static void foo() {
            assert System.out == null;
        }
    }
}

しかし、Outer.Nested.foo()アサーションを有効にして実行すると、アサーション エラー (ステートメントが実行されたため) が表示されますが、Outer initializedメッセージは表示されません。そのため、レキシカルにネストされた assert ステートメントが実行されたとしても、最上位クラスは初期化されませんでした。

ここでの仕様を誤解しているか、言及された実装がそれに従っていません。

論理的根拠について: この要件の主なポイントは、アサーションの有効化と無効化がクラスの非表示の静的 (および iirc. final) フィールドを介して実装されるという事実だと思います。したがって、assert ステートメントが実行されると、そのフィールドをチェックする必要があるため、そのフィールドを初期化する必要があるため、クラスを初期化する必要があります。しかし、上記のコードでは、関連するフィールドは のフィールドであり、それ自体のフィールドではOuter.Nestedない可能性がありOuterます。Outerしたがって、その時点で初期化する必要がないのは理にかなっています。しかし、上記の構成を除けば、最後のルールが適用され、他のルールが適用されないケースは考えられません。

于 2013-07-23T08:59:21.040 に答える