このサイトで別の答えのコードを書いているときに、この特異性に出会いました。
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
呼び出しで文句を言うのですか?彼らは非常に似ているようです。
sneakyThrow
のTはRuntimeException
であると推測されます。これは、型推論に関する言語仕様( http://docs.Oracle.com/javase/specs/jls/se8/html/jls-18.html )の後に続くことができます。
まず、セクション18.1.3に注意事項があります。
throws α
の形式の境界は、単なる情報です。これは、可能であれば、チェックされた例外タイプではないように、αのインスタンス化を最適化するように解決を指示します。
これは何にも影響しませんが、解決セクション(18.4)を示しています。このセクションには、特殊なケースで推論された例外タイプに関する詳細情報があります。
...それ以外の場合、バウンドセットに
throws αi
が含まれ、αiの適切な上限は、最大でException
、Throwable
、およびObject
であり、次に、Ti =RuntimeException
。
このケースはsneakyThrow
に適用されます-唯一の上限はThrowable
であるため、T
は仕様に従ってRuntimeException
であると推測され、コンパイルされます。メソッドの本体は重要ではありません-実際には発生しないため、実行時にチェックされていないキャストが成功し、コンパイル時のチェック済み例外システムを無効にするメソッドが残ります。
nonSneakyThrow
は、そのメソッドのT
がException
の下限を持っているためコンパイルされません(つまり、T
はException
のスーパータイプでなければなりません。またはException
自体)、これは呼び出される型のためにチェック例外であるため、T
はException
として推論されます。
型推論が型変数の単一の上限を生成する場合、通常、上限はソリューションとして選択されます。たとえば、T<<Number
の場合、ソリューションはT=Number
です。 Integer
、Float
なども制約を満たすことができますが、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が上限の特定のサブタイプを選択する正当な理由です。
sneakyThrow
の場合、タイプT
は境界のあるジェネリック型変数ですwithout特定の型(型がどこから来るかがないため)。
nonSneakyThrow
の場合、型T
は引数と同じ型であるため、この例では、nonSneakyThrow(e);
のT
はException
です。 。 testSneaky()
はスローされたException
を宣言しないため、エラーが表示されます。
これは、確認された例外を伴うジェネリック医薬品の既知の干渉であることに注意してください。