Java8の例外型推論の固有の機能


85

このサイトで別の答えのコードを書いているときに、私はこの特異性に出くわしました。

static void testSneaky() {
  final Exception e = new Exception();
  sneakyThrow(e);    //no problems here
  nonSneakyThrow(e); //ERRROR: Unhandled exception: java.lang.Exception
}

@SuppressWarnings("unchecked")
static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
  throw (T) t;
}

static <T extends Throwable> void nonSneakyThrow(T t) throws T {
  throw t;
}

まず、sneakyThrowコンパイラーにとって呼び出しがOKである理由がかなり混乱しています。Tチェックされていない例外タイプのどこにも言及がない場合、どのようなタイプが推測されましたか?

第二に、これが機能することを受け入れると、なぜコンパイラはnonSneakyThrow呼び出しで文句を言うのですか?それらは非常に似ているようです。

回答:


66

のTはであるsneakyThrowと推測されますRuntimeException。これは、型推論に関する言語仕様(http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html)から確認できます。

まず、セクション18.1.3に注意事項があります。

フォームの境界throws αは純粋に情報提供です。αのインスタンス化を最適化するように解決を指示し、可能であれば、チェックされた例外タイプにならないようにします。

これは何にも影響しませんが、解決セクション(18.4)を示しています。このセクションでは、特殊なケースで推測される例外タイプに関する詳細情報を入手しています。

...それ以外の場合は、バインドされたセットが含まれている場合throws αi、およびαiをの適切な上限があり、最大で、ExceptionThrowable、そしてObject、その後、Tiを= RuntimeException

この場合はに適用されますsneakyThrow-唯一の上限はThrowableであるため、仕様どおりであるTと推測されるRuntimeExceptionため、コンパイルされます。メソッドの本体は重要ではありません。チェックされていないキャストは実際には発生しないため、実行時に成功し、コンパイル時にチェックされた例外システムを無効にすることができるメソッドが残ります。

nonSneakyThrowそのメソッドのT下限がException(つまりT、のスーパータイプでExceptionあるか、それException自体である必要があります)であるため、コンパイルされません。これは、呼び出されるタイプが原因でチェックされた例外であるため、Tとして推測されExceptionます。


1
@MaksymあなたはsneakyThrow呼び出しを意味する必要があります。推論に関する特別規則throws TフォームのJava 7の仕様には存在しませんでした
マルコTopolnik

1
小さな落とし穴:はnonSneakyThrow、「のスーパータイプ」ではなく、であるT必要がありますExceptionExceptionこれは、呼び出しサイトでコンパイル時に宣言された引数のタイプであるためです。
llogiq 2015

1
@llogiq仕様を正しく読んだ場合、の下限Exceptionと上限があるThrowableため、結果として推定される型である最小の上限はExceptionです。
thecoop 2015

1
@llogiq引数のスーパータイプも受け入れられるため、引数の型は下限の型境界のみを設定することに注意してください。
Marko Topolnik 2015

2
「またはExceptionそれ自体」というフレーズは読者に役立つかもしれませんが、一般に、仕様では常に「それ自体を含む」という意味で「サブタイプ」および「スーパータイプ」という用語が使用されていることに注意してください…
Holger

17

型推論が型変数の単一の上限を生成する場合、通常、上限が解として選択されます。たとえば、の場合T<<Number、解はT=Numberです。がIntegerFloatまた制約を満たすことができなど、それらを超える選択するは良い理由はありませんNumber

これはthrows T、Java5-7の場合にも当てはまりましたT<<Throwable => T=Throwable。(卑劣なスローソリューションにはすべて明示的な<RuntimeException>型引数があり、それ以外の場合<Throwable>は推測されます。)

java8では、ラムダの導入により、これが問題になります。このケースを考えてみましょう

interface Action<T extends Throwable>
{
    void doIt() throws T;
}

<T extends Throwable> void invoke(Action<T> action) throws T
{
    action.doIt(); // throws T
}    

空のラムダで呼び出すと、何Tが推測されますか?

    invoke( ()->{} ); 

の唯一の制約Tは上限Throwableです。java8の初期段階では、T=Throwableが推測されます。私が提出したこのレポートを参照してください。

しかしThrowable、空のブロックからチェックされた例外を推測するのはかなりばかげています。解決策がレポートで提案されました(これは明らかにJLSによって採用されています)-

If E has not been inferred from previous steps, and E is in the throw clause, 
and E has an upper constraint E<<X,
    if X:>RuntimeException, infer E=RuntimeException
    otherwise, infer E=X. (X is an Error or a checked exception)

つまり、上限がまたはのException場合ThrowableRuntimeException解として選択します。この場合は、そこにある上限の特定のサブタイプを選択するための良い理由が。


X:>RuntimeException最後の例のスニペットの意味は何ですか?
marsouf

1

を使用するsneakyThrowと、型Tは特定の型のない有界のジェネリック型変数になります(型の出所がないため)。

nonSneakyThrow場合、型Tは引数と同じ型であるため、この例では、TofnonSneakyThrow(e);Exceptionです。ASがtestSneaky()スローを宣言しないExceptionと、エラーが表示されます。

これは、チェックされた例外を伴うGenericsの既知の干渉であることに注意してください。


それで、sneakyThrowそれは実際には特定のタイプに推論されておらず、「キャスト」はそのような未定義のタイプにあるのでしょうか?これで実際に何が起こるのだろうか。
Marko Topolnik 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.