13

インスタンスの初期化 (クラスの初期化ではない) ブロックで例外をスローしようとすると、次のエラーが発生します。

initializer must be able to complete normally

Javaはそれ自体を行うのに、なぜ許可されないのですか?

次の例では、4 つのクラスを作成します。ArithmeticException が原因で、インスタンス化中にクラスAが失敗します。これは で処理できますcatch。同じことBが NullPointerException で失敗します。しかし、プログラムのように自分で NullPointerException をスローしようとすると、Cコンパイルされません。そして、独自の RuntimeException を のように定義しようとすると、同じエラーが発生しますD。そう:

Java 自体と同じことを行うにはどうすればよいですか?

// -*- compile-command: "javac expr.java && java expr"; -*-

class expr
{
    class A
    {
        int y;
        {{ y = 0 / 0; }}
    }

    class B
    {
        Integer x = null;
        int y;
        {{ y = x.intValue(); }}
    }

    class C
    {
        {{ throw new NullPointerException(); }}
    }

    class Rex extends RuntimeException {}

    class D
    {
        {{ throw new Rex(); }}
    }

    void run ()
    {
        try { A a = new A(); }
        catch (Exception e) { System.out.println (e); }

        try { B b = new B(); }
        catch (Exception e) { System.out.println (e); }

        try { C c = new C(); }
        catch (Exception e) { System.out.println (e); }

        try { D d = new D(); }
        catch (Exception e) { System.out.println (e); }
    }

    public static void main (String argv[])
    {
        expr e = new expr();
        e.run();
    }
}
4

7 に答える 7

19

初期化子は正常に完了できる必要があります

例外をスローしない可能性のあるコード パスが存在する必要があることを意味します。あなたの例は無条件にスローされるため、拒否されます。他の例では、静的分析は、すべてのケースでスローすることを決定するのに十分ではありません。

例えば、

public class StaticThrow {
    static int foo = 0;
    {{ if (Math.sin(3) < 0.5) { throw new ArithmeticException("Heya"); } else { foo = 3; } }}
    public static void main(String[] args) {
        StaticThrow t = new StaticThrow();
        System.out.println(StaticThrow.foo);
    }
}

コンパイルし、実行時にスローします

Exception in thread "main" java.lang.ArithmeticException: Heya
        at StaticThrow.<init>(StaticThrow.java:3)
        at StaticThrow.main(StaticThrow.java:5)
于 2012-11-23T16:33:46.363 に答える
5

Java は最小限の機能を持つように設計されており、複雑さは非常に正当な理由がある場合にのみ追加されます。Java は質問しません。なぜ、それは尋ねます。本当にこれをサポートする必要がありますか? (そして、それでも時々そうではありません;)

初期化ブロックのコードを各コンストラクターに挿入する必要があります。これには、コンパイラーがコードを生成するのが難しすぎると判断した状態で正常に完了しないことをコンパイラーが認識しているブロックがあります。

コンパイラでこのコードをコンパイルすることはできますが、役に立たない可能性があります。


この特定のケースでは役に立ちませんが、知っておくと役に立ちます.....

チェック済み例外は宣言する必要があり、静的またはインスタンス初期化ブロックでチェック済み例外を宣言する方法はありません。

代わりに、チェックされた例外をキャッチして処理したり、ラップしたりできます。(またはトリックを使用して、再スローします)

于 2012-11-23T16:32:59.933 に答える
3

実際には初期化ブロックで例外をスローできますが、例外がチェックされている場合は、すべてのコンストラクターを「throws」キーワードでマークする必要があります。

例外が常にスローされる場合、コンパイル エラーが発生しますが、次のようなものは完全に合法です。

クラス Foo {

{{
    if(1 == 1) {
        throw new Exception();
    }
}}

public Foo() throws Exception {

}

}

これでいくつかのことが明確になることを願っています。

于 2012-11-23T16:49:15.137 に答える
2
{ throw new Rex(); }

これは、インスタンスが適切に初期化されないことを意味します。インスタンスを適切に初期化できる条件が必要です。例えば

{ if(true) { throw new Rex(); } } //It doesn't complain here

スローされる例外がチェック例外である場合は、それをコンストラクターのthrows句に追加する必要があります。例えば

public class MyObject {
    { 
        //...
            throw new Exception();
        //...
    }

    public MyObject() throws Exception {

    }
}
于 2012-11-23T16:41:42.657 に答える
1

これは、Java 言語仕様 (Java SE 7) のセクション 8.6でカバーされています。

インスタンス初期化子が正常に完了できない場合は、コンパイル時エラーです ( §14.21 )。

14.21 では、到達不能とはどういう意味かを定義しています。特に注意

switch ブロックではない空でないブロック内の他のすべてのステートメント S は、S の前のステートメントが正常に完了できる場合に到達可能です。

break、continue、return、または throw ステートメントが正常に完了できません。

より複雑な分析が可能です (それでも警告が生成される可能性があります) が、これらは理解可能で、一貫して実装可能であり、言語の将来の開発を特に制限しない一連のルールです。

では、なぜ (確実に) 到達不能なステートメントを含むプログラムを拒否したいのでしょうか? それらはほぼ確実に (完成したコードで) バグを表しているためです。(ifステートメントは、危険な条件付きコンパイルをサポートするために独特に動作します。)

到達不能なステートメントはないのに、インスタンス初期化子が正常に完了できる必要があるのはなぜですか (インスタンス化できないクラスをサポートするためのコンストラクターの要件ではありません)。これには、Java が合理的にシンプルな状態を維持するために行わない非ローカル分析が必要であり、メンテナンス中にステートメントが削除されたり、コードの順序が変更されたりする可能性があるためです。

Java は、この比較的単純な分析と定義代入規則によって過度に複雑であると考える意見が集まっていることは、おそらく注目に値するでしょう。

于 2012-11-24T12:41:21.610 に答える
1

http://www.artima.com/designtechniques/initializationP.htmlから

インスタンス初期化子内のコードは返されない場合があります。匿名内部クラスの場合を除いて、インスタンス初期化子は、クラス内のすべてのコンストラクターの throws 句でチェック済み例外が明示的に宣言されている場合にのみ、チェック済み例外をスローできます。一方、匿名内部クラスのインスタンス初期化子は、あらゆる例外をスローできます。

于 2012-11-23T16:57:24.467 に答える
0

これにより、残りのステートメントは明らかに到達不能になり、Javaはこれを禁止しようとします。

于 2012-11-23T18:45:11.007 に答える