Javaが静的初期化ブロックからチェック例外をスローできないのはなぜですか?


135

Javaが静的初期化ブロックからチェック例外をスローすることを許可しないのはなぜですか?この設計決定の背後にある理由は何でしたか?


静的ブロックでどのような状況でどのような例外をスローしたいですか?
Kai Huppmann、2010年

1
そんなことはしたくない。静的ブロック内のチェックされた例外をキャッチすることがなぜ必須であるのかを知りたいだけです。
missingfaktor 2010年

次に、チェックされた例外がどのように処理されると思いますか?気になる場合は、スローされた例外をthrow new RuntimeException( "Telling message"、e);で再スローするだけです。
–ThorbjørnRavn Andersen、2010

18
:ThorbjørnRavnAndersenJavaは、実際にそのような状況のための例外の種類を提供@ docs.oracle.com/javase/6/docs/api/java/lang/...
smp7d

@ smp7d以下のkevinarpeの回答と、StephenCからのコメントを参照してください。それは本当にクールな機能ですが、トラップがあります!
ベンジ2017年

回答:


122

ソースでこれらのチェック済み例外を処理することができないためです。初期化プロセスを制御することはできず、静的{}ブロックをソースから呼び出すことができないため、ブロックをtry-catchで囲むことができます。

チェックされた例外が示すエラーは処理できないため、チェックされた例外の静的ブロックのスローを禁止することにしました。

静的ブロックは、チェックされた例外をスローしてはなりませんが、チェックされていない/実行時例外をスローすることを許可します。しかし、上記の理由によると、これらのいずれも処理できません。

要約すると、この制限により、開発者がアプリケーションを回復できないエラーを引き起こす可能性のある何かを構築することが防止されます(または少なくとも困難になります)。


69
実際には、この答えは不正確です。静的ブロックで例外をスローできます。あなたができないことは、チェックされた例外が静的ブロックから伝播することを許可することです。
スティーブンC

16
Class.forName(...、true、...);を使用して動的にクラスをロードしている場合は、この例外を処理できます。確かに、これはあなたが頻繁に遭遇するものではありません。
LadyCailin

2
static {新しいNullPointerExcpetion()をスローする}-これもコンパイルされません!
キリルバザロフ2014年

4
@KirillBazarov常に例外が発生する静的イニシャライザを持つクラスはコンパイルされません(なぜそれが必要なのですか?)。ステートメントをスローする場合はif節で囲み、準備完了です。
Kallja 14

2
@Ravisha。その場合、初期化子がどのような場合でも正常に完了する可能性がないためです。try-catchを使用すると、printlnによって例外がスローされない場合があるため、イニシャライザは例外なく完了する可能性があります。コンパイルエラーになる例外の無条件の結果です。そのためJLSを参照してください:docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.7 しかし、コンパイラは、まだあなたのケースで簡単な条件を追加することによってだまされる可能性がありますstatic { if(1 < 10) { throw new NullPointerException(); } }
Kosi2801

67

チェックされた例外をキャッチし、チェックされていない例外として再スローすることで、問題を回避できます。このチェックされていない例外クラスは、ラッパーとしてうまく機能しますjava.lang.ExceptionInInitializerError

サンプルコード:

protected static class _YieldCurveConfigHelperSingleton {

    public static YieldCurveConfigHelper _staticInstance;

    static {
        try {
            _staticInstance = new YieldCurveConfigHelper();
        }
        catch (IOException | SAXException | JAXBException e) {
            throw new ExceptionInInitializerError(e);
        }
    }
}

1
@DK:Javaのバージョンがこのタイプのキャッチ節をサポートしていない可能性があります。試してください:catch (Exception e) {代わりに。
kevinarpe

4
はい、これを行うことができますが、それは本当に悪い考えです。チェックされていない例外は、クラスと、そのクラスにアンロードすることによってのみ解決できる障害状態に依存する他のクラスを配置します。それは一般的に不可能であり、およびSystem.exit(...)(または同等の)は、あなたの唯一のオプションである
スティーブンC

1
@StephenCは、「親」クラスのロードに失敗した場合、コードが機能しないため、その依存クラスをロードする必要は事実上必要ないと考えることができますか?とにかくそのような依存クラスをロードする必要がある場合の例をいくつか教えていただけませんか?ありがとう
Benj 2017年

コードが動的にロードしようとした場合はどうでしょうか。たとえばClass.forNameを介して。
スティーブンC

21

次のようにする必要があります(これは有効なJavaコードではありません

// Not a valid Java Code
static throws SomeCheckedException {
  throw new SomeCheckedException();
}

しかし、あなたはそれをどこでキャッチするか?チェックされた例外はキャッチする必要があります。クラスを初期化する可能性のあるいくつかの例を想像してください(または、クラスが既に初期化されているため、そうでない場合があります)。それがもたらす複雑さの注意を引くために、別の静的初期化子に例を置きます。

static {
  try {
     ClassA a = new ClassA();
     Class<ClassB> clazz = Class.forName(ClassB.class);
     String something = ClassC.SOME_STATIC_FIELD;
  } catch (Exception oops) {
     // anybody knows which type might occur?
  }
}

そしてもう一つの厄介なこと-

interface MyInterface {
  final static ClassA a = new ClassA();
}

ClassAに静的イニシャライザがチェックされた例外をスローしていると想像してください。この場合、MyInterface(「非表示」の静的イニシャライザを持つインターフェイス)は、例外をスローするか、それを処理する必要があります-インターフェイスでの例外処理?そのままにしておくとよいでしょう。


7
mainチェックされた例外をスローできます。明らかにそれらを処理することはできません。
機械式カタツムリ

@Mechanicalsnail:興味深い点。私のJavaのメンタルモデルでは、実行中のスレッドに「魔法の」(デフォルト)Thread.UncaughtExceptionHandlerが接続されておりmain()、スタックトレースがに例外が出力されてからが呼び出されると想定していSystem.errますSystem.exit()。結局のところ、この質問に対する答えはおそらく「Javaデザイナーがそう言ったから」です。
kevinarpe 2017年

7

Javaが静的初期化ブロックからチェック例外をスローすることを許可しないのはなぜですか?

技術的には、これを行うことができます。ただし、チェックされた例外はブロック内でキャッチする必要があります。チェックされた例外は、ブロックの外に伝播することはできません。

技術的には、チェックされていない例外が静的初期化ブロック1から伝播することを許可することもできます。しかし、これを意図的に行うのは本当に悪い考えです!問題は、JVM自体がチェックされていない例外をキャッチし、それをラップしてとして再スローすることExceptionInInitializerErrorです。

注意:これはError通常の例外ではありません。そこから回復しようとするべきではありません。

ほとんどの場合、例外はキャッチできません。

public class Test {
    static {
        int i = 1;
        if (i == 1) {
            throw new RuntimeException("Bang!");
        }
    }

    public static void main(String[] args) {
        try {
            // stuff
        } catch (Throwable ex) {
            // This won't be executed.
            System.out.println("Caught " + ex);
        }
    }
}

$ java Test
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Bang!
    at Test.<clinit>(Test.java:5)

try ... catch上のExceptionInInitializerError2をキャッチできる場所はありません。

場合によってはそれをキャッチすることができます。たとえばClass.forName(...)、を呼び出してクラスの初期化をトリガーした場合、で呼び出しを囲みtryExceptionInInitializerErrorまたはのいずれかをキャッチできますNoClassDefFoundError

ただし、回復を試みた場合ExceptionInInitializerError、ロードブロッキングが発生する可能性があります。問題は、エラーをスローする前に、JVMが問題の原因となったクラスを「失敗」としてマークすることです。使用できなくなります。さらに、失敗したクラスに依存する他のクラスも、初期化しようとすると失敗状態になります。前進する唯一の方法は、失敗したクラスをすべてアンロードすることです。これ、動的にロードされるコード3で実現可能かもしれませんが、一般的には不可能です。

1-静的ブロックが無条件に未チェックの例外をスローする場合、これはコンパイルエラーです。

2- デフォルトのキャッチされない例外ハンドラーを登録することでそれをインターセプトできる可能性ありますが、「メイン」スレッドが開始できないため、回復できません。

3-失敗したクラスを回復したい場合、それらをロードしたクラスローダーを取り除く必要があります。


この設計決定の背後にある理由は何でしたか?

これは、処理できない例外をスローするコードをプログラマが作成しないようにするためです。

これまで見てきたように、静的イニシャライザの例外は、一般的なアプリケーションをレンガに変えます。言語設計者ができる最善のことは、チェックされたケースをコンパイルエラーとして処理することです。(残念ながら、チェックされていない例外に対してもこれを行うのは現実的ではありません。)


OK、コードが静的初期化子で例外をスローする必要がある場合はどうすればよいでしょうか。基本的に、2つの選択肢があります。

  1. ブロック内の例外からの(完全!)回復が可能な場合は、それを実行します。

  2. それ以外の場合は、静的初期化ブロック(または静的変数の初期化子)で初期化が行われないようにコードを再構成します。


静的な初期化を行わないようにコードを構造化する方法に関する一般的な推奨事項はありますか?
MasterJoe2

これらのソリューションはどのように聞こえますか?stackoverflow.com/a/21321935/6648326およびstackoverflow.com/a/56575807/6648326
MasterJoe2

1
1)ありません。2)音が悪い。私が彼らに残したコメントを見てください。しかし、私は上記の私の答えで言ったことを繰り返すだけです。私の回答を読んで理解すれば、それらの「解決策」は解決策ではないことがわかるでしょう。
スティーブンC

4

Java言語仕様を見てください。静的なイニシャライザが失敗した 場合に、コンパイル時エラーであり、チェックされた例外で突然終了する可能性があると記載されています。


5
しかし、これは質問には答えません。それがなぜコンパイル時エラーであるのと彼は尋ねました。
ウィンストンスミス

うーん、JLSはチェックされた例外にのみ言及しているため、RuntimeErrorをスローすることは可能なはずです。
Andreas Dolk、2010年

その通りですが、スタックトレースとして表示されることはありません。そのため、静的初期化ブロックに注意する必要があります。
EJB

2
@EJB:これは不正解です。私はちょうどそれを試してみたし、次のコードは、私に視覚的なスタックトレースを与えた:public class Main { static { try{Class.forName("whathappenswhenastaticblockthrowsanexception");} catch (ClassNotFoundException e){throw new RuntimeException(e);} } public static void main(String[] args){} }出力:Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: whathappenswhenastaticblockthrowsanexception at Main.<clinit>(Main.java:6) Caused by: java.lang.ClassNotFoundException: whathappen...
コンラートHöffner

一部のショーあなたはおそらくもっと興味を持っているスタックトレースを「によって引き起こされる」。
LadyCailin

2

記述したコードは静的初期化ブロックを呼び出すことができないため、checkedをスローすることは役に立ちませんexceptions。可能であれば、チェックされた例外がスローされたときにjvmは何をしますか?Runtimeexceptions伝達されます。


1
ええ、私は今、事を理解しています。このような質問を投稿するのはとてもばかげていました。しかし、残念ながら、今は削除できません。:(それにもかかわらず、あなたの応答に+1します...
missingfaktor

1
@fast、実際には、チェックされた例外はRuntimeExceptionsに変換されません。自分でバイトコードを書く場合、静的イニシャライザ内のチェックされた例外を心ゆくまで投げることができます。JVMは例外チェックをまったく気にしません。純粋にJava言語の構成要素です。
アンチモン2012年

0

例:SpringのDispatcherServlet(org.springframework.web.servlet.DispatcherServlet)は、チェックされた例外をキャッチし、別のチェックされていない例外をスローするシナリオを処理します。

static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }

1
このアプローチは、チェックされていない例外をキャッチできないという問題に対処します。代わりに、クラスとそれに依存する他のクラスを回復不可能な状態にします。
スティーブンC

@StephenC-回復可能な状態にしたい簡単な例を教えてください。
MasterJoe2

仮説...アプリケーションを続行できるようにIOExceptionから回復できるようにする場合。それを行う場合は、例外をキャッチして実際に処理する必要があります。チェックされていない例外をスローしないでください。
スティーブンC

-5

チェックした例外をスローしてコンパイルすることもできます...

static {
    try {
        throw new IOException();
    } catch (Exception e) {
         // Do Something
    }
}

3
ええ、でもあなたは静的ブロック内でそれをキャッチしています。静的ブロックの内部から外部にチェック例外をスローすることはできません。
ArtOfWarfare 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.