web-dev-qa-db-ja.com

Java 8の例外タイプ推論の独特な機能

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

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呼び出しがコンパイラにとって問題ない理由を私はかなり混乱しています。チェックされていない例外タイプのどこにも言及がない場合、Tに対してどのようなタイプを推論しましたか?

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

82
Marko Topolnik

sneakyThrowのTは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は、そのメソッドのTExceptionの下限を持っているためコンパイルされません(つまり、TExceptionのスーパータイプでなければなりません。またはException自体)、これは呼び出される型のためにチェック例外であるため、TExceptionとして推論されます。

63
thecoop

型推論が型変数の単一の上限を生成する場合、通常、上限はソリューションとして選択されます。たとえば、T<<Numberの場合、ソリューションはT=Numberです。 IntegerFloatなども制約を満たすことができますが、Numberよりもそれらを選択する正当な理由はありません。

Java 5-7:throws T。]の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が推測されます。こちらをご覧ください report 提出しました。

しかし、空のブロックからチェック例外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またはThrowableである場合、解としてRuntimeExceptionを選択します。この場合、isが上限の特定のサブタイプを選択する正当な理由です。

16
ZhongYu

sneakyThrowの場合、タイプTは境界のあるジェネリック型変数ですwithout特定の型(型がどこから来るかがないため)。

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

これは、確認された例外を伴うジェネリック医薬品の既知の干渉であることに注意してください。

1
llogiq