24
class Bouncy<T> extends Throwable {     
}
// Error: the generic class Bouncy<T> may not subclass java.lang.Throwable

Java がジェネリックをサポートしないThrowableのはなぜですか?

型の消去が特定のことを複雑にしていることは理解していますが、明らかに Java はすでに多くのことを行っています。それをさらに押し上げて、ジェネリックを許可Throwableし、潜在的な問題を包括的にコンパイル時にチェックしてみませんか?


型消去の引数はかなり弱いように感じます。現在、次のことはできません。

void process(List<String> list) {
}

void process(List<Integer> list) {
}

もちろん、それなしでやっていけます。同じブロック内でcatch Bouncy<T1>とを実行できるようにする必要があるとは言いませんが、厳密なコンパイル時の強制可能なルール (現在のジェネリックの動作とほとんど同じです) を使用してばらばらなコンテキストでそれらを使用すると、そうではありませんか?実行可能ですか?Bouncy<T2>try

4

5 に答える 5

9

簡単な答え: 消去の場合と同じように、ショートカットを使用したためです。

長い答え: 他の人が既に示したように、消去のために、「catch MyException<String>」と「catch MyException<Integer>」の間で実行時に違いを生む方法はありません。

しかし、それは一般的な例外が必要ないという意味ではありません。ジェネリックでジェネリック フィールドを使用できるようにしたい! 単純に一般的な例外を許可することもできますが、生の状態でキャッチすることだけを許可します (例: "catch MyException")。

確かに、これによりジェネリックはさらに複雑になります。これは、ジェネリックを消去するという決定がいかに悪いものであったかを示すためのものです。現在のシンタックス シュガーではなく、実際のジェネリック (RTTI を使用) をサポートする Java バージョンはいつ提供されるのでしょうか?

于 2012-01-17T08:09:13.290 に答える
8

型消去。ランタイム例外タイプにはジェネリック情報がありません。したがって、あなたはすることはできません

} catch( Mistake<Account> ea) {
  ...
} catch( Mistake<User> eu) {
...
}

あなたにできること

catch( Mistake ea ) {
  ...
}

そして、型消去は、Javaが1.4から1.5に移行したときに下位互換性を維持することが決定された方法です。その時、多くの人が不幸でした、そして当然のことながらそうです。しかし、デプロイされたコードの量を考えると、1.4でうまく機能したコードを壊すことは考えられませんでした。

于 2010-03-15T02:27:43.310 に答える
5

次のように、引き続きジェネリック メソッドを使用できます。

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 仕様を参照している限り)、Cristian Vasile の回答は、制限を満たし、さらには挑戦するものです。

この質問への回答で、私が同意せず反論する議論が少なくとも 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)andを処理することはできませんでしcatch(Exception<T2> e)たが、それは今日のメソッド引数または戻り値よりも悪いことではありません:今日もmyMethod(List<T1>)andを処理しませんmyMethod(List<T2>)... (これを繰り返します)以下の 2 番目の反論の側面。)


2 番目の引数は次のようになります。これは許可されません:

catch (Exception<T1> e) {}

これはうまくいかないので:

catch (Exception<T1> e) {}
catch (Exception<T2> e) {}

では、これを禁止しない理由は次のとおりです。

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);
}

言い換えれば、ほとんどの場合ジェネリックを禁止しないのはなぜでしょうか?

この 2 番目の引数は、これらのコンテキストで同じ非ジェネリック コンストラクトを消去するさまざまなジェネリック コンストラクトを許可することはできませんが、型が消去されない限り、次善の策を講じてジェネリックを許可できることを忘れています。同じタイプに。これがメソッド パラメーターで行うことです。ジェネリックの使用を許可しますが、型消去後に重複するシグネチャが検出されるとすぐに文句を言います。まあ、例外とキャッチブロックで同じことをすることもできたでしょう...


結論として、私はクリスチャンの答えを拡張します。一般的な例外クラスをcatch許可し、ブロックで生の型を使用する代わりに:

class MyException<T> {}
...
catch (MyException e) { // raw

Java は問題なく実行できたはずです。

class MyException<T> {}
...
catch (MyException<Foo> e) {
于 2012-08-15T20:59:21.843 に答える
3

できることは次のとおりです。

  1. スロー可能オブジェクト自体が型パラメーターを持たない限り、スロー可能オブジェクトはジェネリック インターフェイスを実装できます。

    interface Bouncy<E> {
        // ...
    }
    class BouncyString extends Exception implements Bouncy<String> {
        // ...
    }

  2. throws句は型パラメータを参照できます。
    static <X extends Throwable> void
    throwIfInstanceOf(Throwable ex, Class<X> clazz) throws X {
        if (clazz.isInstance(ex)) throw clazz.cast(ex);
    }

于 2011-05-21T14:28:49.873 に答える