なぜ Java は汎用 Throwable をサポートしないのですか?
質問
class Bouncy<T> extends Throwable {
}
// Error: the generic class Bouncy<T> may not subclass java.lang.Throwable
Java がジェネリックをサポートしないのはなぜですか Throwable
は?
型の消去によって特定のことが複雑になることは承知していますが、明らかに Java はすでに多くのことをうまくやっています。それをもう 1 段階押し上げてジェネリックを許可してはどうでしょうか Throwable
潜在的な問題をコンパイル時に包括的にチェックしますか?
型消去の議論はかなり弱いような気がします。現在、次のことはできません:
void process(List<String> list) {
}
void process(List<Integer> list) {
}
もちろん、それがなくても大丈夫です。私たちができるべきだと言っているのではありません catch Bouncy<T1>
そして Bouncy<T2>
同じように try
ブロックですが、厳密なコンパイル時に適用可能なルール (これが現時点でのジェネリックスの動作とほとんど同じです) を使用して、素のコンテキストでそれらを使用する場合、それは機能するのではないでしょうか?
解決
Java言語仕様 8.1.2ジェネリッククラスと型パラメータ:
Java仮想マシンのキャッチ機構は、非ジェネリッククラスでのみ動作しますので、この制限は必要とされます。
個人的に、私たちはcatch
句内のジェネリック医薬品のあらゆるメリットを得ることができないので、それはだと思います。私たちは、消去を入力によるcatch (Bouncy<String> ex)
を書くことはできませんが、我々はcatch (Bouncy ex)
を書く場合は、一般的な作るために役に立たないだろう。
他のヒント
消去を入力します。ランタイム例外タイプには、ジェネリック医薬品の情報を持っていません。したがって、あなたが行うことができないの の
} catch( Mistake<Account> ea) {
...
} catch( Mistake<User> eu) {
...
}
あなたはすべて行うことができます。
でありますcatch( Mistake ea ) {
...
}
そして、型消去は、Javaが1.4から1.5に移動していたの下位互換性を維持することを決定した方法です。多くの人々は、その後不幸だった、と当然のようにします。しかし、心の中で展開されたコードの量を持つ、1.4に喜んで働いていたコードを破ることは考えられないことだった。
短い答え:消去の場合と同じように、ショートカットをしたからです。
長い答え:他の人がすでに指摘したように、消去のため、実行時に「catch MyException<String>」と「catch MyException<Integer>」を区別する方法はありません。
しかし それは一般的な例外が必要ないという意味ではありません. 。ジェネリックでジェネリックフィールドを使用できるようにしたい!単純に一般的な例外を許可することもできますが、生の状態でのみキャッチできるようにすることもできます (例:「MyException をキャッチ」)。
確かに、これによりジェネリックはさらに複雑になります。これは、ジェネリック医薬品を消去するという決定がいかに間違っていたかを示すものです。現在の構文シュガーではなく、実際のジェネリックス (RTTI を使用) をサポートする Java バージョンはいつ登場するのでしょうか?
あなたはまだこのような一般的な方法を、使用することができます
public class SomeException {
private final Object target;
public SomeException(Object target) {
this.target = target;
}
public <T> T getTarget() {
return (T) target;
}
}
....
catch (SomeException e) {
Integer target = e.getTarget();
}
私は上記のクリスティアンの答えに同意します。受け入れ答えは(それがJVM仕様を参照する限りにおいては)技術的に正しいですが、クリスティアン・バサイルの答えは資格さえ制限に挑戦するものです。
私は私が、私は反論することを同意しないこの質問への回答で述べた2つの引数が少なくともあります。これらの答えで引数が正しかった場合、我々は、彼らが今日成功裏に使用されている他のコンテキストでジェネリックを攻撃するためにこれらの引数を使用することができます。
<時間>私たちはこれを使用することはできません最初の引数の状態:
catch (Exception<T1> e) {}
JVMはException<T1>
と連携する方法を知らないので。この引数は、JVMがList<T1>
を使用する方法を知らないということに基づいて、また、ジェネリック医薬品のこの使用を攻撃するように思われる。
List<T1> list;
引数は、もちろん、コンパイラは消去を入力し実行し、そのJVMはException<T1>
を処理する方法を知っている必要はないことを忘れてしまいます。それは単にそれがException
を扱う同じように、List
を扱うことができます。
もちろん、我々は理由型消去の同じのtry / catchでcatch(Exception<T1> e)
とcatch(Exception<T2> e)
を扱うことができませんでしたが、その後、再び、それは、メソッドの引数を持つよりも悪化しませんか返す:我々は処理しないmyMethod(List<T1>)
とmyMethod(List<T2>)
今日のいずれか...(私は以下の第二反論でこの態様を繰り返す。)
次のように2番目の引数は行きます。
:私たちは、このことはできません。catch (Exception<T1> e) {}
これが動作しないのでます:
catch (Exception<T1> e) {}
catch (Exception<T2> e) {}
OK、なぜこれを禁止ません。
interface MyInterface {
Comparable<Integer> getComparable();
}
これはを動作しませんのため
interface MyInterface {
Comparable<Integer> getComparable();
Comparable<String> getComparable();
}
またはこの:
interface MyInterface {
void setComparable(Comparable<Integer> comparable);
}
これはを動作しませんのため
interface MyInterface {
void setComparable(Comparable<Integer> comparable);
void setComparable(Comparable<String> comparable);
}
言い換えれば、なぜほとんどの場合、ジェネリックを禁止していない?
この二番目の引数には、我々はまだ、が許可ジェネリック限り、次善の策とを行うことができ、我々は、おそらくそれは、これらのコンテキストで同じ非ジェネリック構造に消去することを別の一般的な構文を許可しませんでしたが、ことを忘れ種類は、同じタイプのには消去されません。それは我々がメソッドのパラメータで何をすべきかです:私たちは、あなたがジェネリックを使用することができますが、できるだけ早く我々は型消去した後に、重複する署名を検出するよう訴えます。まあ、我々は例外とcatchブロックで同じことを行っている可能性...
<時間>結論として、私はクリスティアンの答えに拡大します。代わりに、許可の一般的な例外クラスは、<全角>とのcatch
ブロックで生タイプを使用します:
class MyException<T> {}
...
catch (MyException e) { // raw
Javaは問題なく、全体の方法を行っている可能性がます:
class MyException<T> {}
...
catch (MyException<Foo> e) {
ここでいくつかのことを説明します できる する:
- スロー可能オブジェクト自体に型パラメータがない限り、スロー可能オブジェクトは汎用インターフェイスを実装できます。
interface Bouncy<E> {
// ...
}
class BouncyString extends Exception implements Bouncy<String> {
// ...
}
- あ
throws
句は型パラメータを参照できます。例:static <X extends Throwable> void
throwIfInstanceOf(Throwable ex, Class<X> clazz) throws X {
if (clazz.isInstance(ex)) throw clazz.cast(ex);
}