2013-08-09 9 views
11

ジェネリックスを使っていろいろなことを知りました。以下の例では、doStuff1コンパイルしかしdoStuff2ません:Java:有界型のgetClass()

public <T extends Foo> void doStuff1(T value) { 
    Class<? extends Foo> theClass = value.getClass(); 
} 

public <T extends Foo> void doStuff2(T value) { 
    Class<? extends T> theClass = value.getClass(); 
} 

だから、私はObject.getClass()のドキュメントを見て、これを見つけた:

実際の結果型がクラス<のですか?拡張| X | >ここで、| X | getClassが呼び出される静的型の式の消去です。

これはちょっと好奇心が強い。 getClass()はなぜこのように設計されていますか?該当する場合、タイプを生のクラスに変換することは理解できますが、どうしてそれらも必ずTを殺すようにしなければならないという明白な理由はありません。それはまたそれを取り除く特定の理由はありますか、それはちょうど一般的な "それは簡単ですので、すべてを取り除いてみましょう、誰がそれはとにかくそれを必要とする"アプローチ?

+1

これを理解すると、頭が痛くなります。あまり遅くなくてもジェネリック型境界を計算することは嫌いです。 [JLS for 'Object'](http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.3.2)から検索をお勧めしますルール。これはおそらく、JVMがオブジェクトのランタイムクラスを反映するためにそのような魔法のような魔法をどのように行うかの成果物でしょう。 – chrylis

答えて

7

getClass()戻りClass<? extends X>、本当に悪い何も起こらないことができれば、実際に

唯一の問題は、理論的には正しいことではありません。オブジェクトがArrayList<String>の場合、そのclassはにできません- そのようなクラスはありません。Class<ArrayList>しかありません。

これは実際に消去には関係しません。ある日Javaが完全修飾型を取得した場合、getClass()はまだClass<? extends |X|>を返します。しかし、より詳細なType<? extends X>を返すことができるgetType()のような新しいメソッドが必要です。 Class<? extends X>は、多くのケースにおいて有用である可能性があるため、私たちは

ことない当社独自の方法を設計することができ、タイミングであることのため

を(ただし、getTypeは自分のgetTypeメソッドを持つ既存のクラスの多くと競合する可能性があります)

static <X> Class<? extends X> myGetClass(X x){ ... } 

しかし、標準のlibにこのような種類のハッキングを入れないのは分かります。

+1

"" ... ""の代わりに何を入れますか?チェックされていないキャストですか? – 5gon12eder

+0

@ 5gon12eder - はい。この方法は基本的に安全ではありません。呼び出し側は特定のユースケースで意味をなさないことを確認する必要があります。 – ZhongYu

-1

あなたはvalueが、また、valueの「本当の」クラスを返すんT. getClass()のサブクラスである可能性があり、実際にTであることを確認することはできませんが、あなたはそれがTだ確認することはできませんが、therforeそれが読まなければなりませんClass<? extends T>!これは、Tが型パラメータであるという事実とは関係がありません。

[編集] わかりませんが、私はmethod2がコンパイルされていないことに気付きませんでした。私はこれがコンパイラの問題だと思う。私は得る

Type mismatch: cannot convert from Class<capture#1-of ? extends Foo> to Class<? extends T> 

私はコンパイラがTオブジェクトがFooであることを認識していると思う。コンパイラはgetClass()Class<? extends T>を返すことを知っておく必要がありますが、それはClass<? extends Foo>を返すことだけを知っているようです。それはFooがTのerasure of the static typeであることを意味しますか?

+1

あなたとあなたのポイントは?'Class 'は私が考えているコードの例ですが、そうではありません。問題はgetClass()が(一見)出力された型の過保護である理由です。 – Smallhacker

+0

私の悪い私はあなたの2番目の方法がコンパイルされていないことを確認するのに十分注意深く読まなかった。私はどういうわけか、戻り値がクラスの代わりにクラスである理由について質問していたと思っています...それは本当に変わったと思います。私はそれがコンパイラとは何かを持っていると思いますが、getClass()では何もありません。おそらく、コンパイラはTをObjectに消去していますが、最初のケースで静的型がFooである必要があることを知っていますか? 2番目のケースでは、クラス = FooObject.getClass()が読み込まれますが、これは間違っています。 ?! – kutschkem

-1

非常に良い質問です。

Javaジェネリックの問題は、古い仮想マシンには存在しないということです。 Sunは一般的なサポートを追加することを決めましたが、既存の仮想マシンはそれらをサポートしていませんでした。

新しいJavaをリリースすると、古いバーチャルマシンは最新バージョンのJava用に作成されたコードを実行できなくなり、古いものをレガシーにサポートできなくなるため、大きな問題になる可能性があります仮想マシン。

そこで、彼らは古い仮想マシンでも実行できるようにジェネリックを実装することに決めました。 彼らはBYTECODEから消去することにしました。

したがって、ストーリー全体はjvmsのバックサポートに関するものです。

編集:これは多分質問とは関係ありませんでした。 kutschkemの答えを見てください!

また、私の推測では、2番目のケースでは、キャストはClass<? extends List>からClass<? extends T>です。 このキャストは、ListがTを直接拡張する場合(暗黙のキャストなので)のみ可能です。しかし、その逆は、TはListを拡張します。

私が言うように、その同じことを考える:

String s = "s"; 
Object o = s; 

上記のケースでは、文字列オブジェクトを拡張しているため、キャストが可能です。逆の場合は、明示的なキャストが必要です。有界タイプの

public <T extends List> void doStuff2(T value) { 
    Class<? extends T> theClass = (Class<? extends T>) value.getClass(); 
} 
+1

この旧バージョンとの互換性により、これを破るよりも多くの問題が発生した場合がありますが、これがSunの決定事項です。 ;-) – chrylis

+0

100%合意しました。これはおそらく私がJavaについて本当に嫌い唯一のものです^^ – Lake

+0

この質問はタイプ消去についてではなく、クラス階層に関するものです。私の答えを見てください。 Tが型パラメータではなく、最終的でないクラスであっても、getClass()は依然としてクラスを返します。 kutschkem

-2

guava reflectionツールはこの制限を回避し、タイプ(そうクラス)を取得するためにあなたを助けるためにいくつかのツールを提供します。

あなたがプログラムに明示的なキャストを追加した場合、それはコンパイル。

としては、この説明のページの最初の行で述べている:

型消去のため、あなたは 実行時に一般的なClassオブジェクトの周りに渡すことはできません - あなたはそれらをキャストし、彼らをふりすることができるかもしれませんジェネリック、 しかし、実際にはそうではありません。

Guavaはリフレクションベースのトリックを使用して、実行時にも ジェネリック型を操作して照会できるTypeTokenを提供します。

考えられるのは、境界型のTypeTokenを取得(または作成)し、その型を尋ねることです。

質問に直接答えることができないため(「理由」の部分)、コンパイル不可能な方法を修正できるので、コードを解決できます質問、なぜ一部にあなたに答えを与えるために、そして確かに「Javaのを探している他の人々を助ける: - どのように(-getClassする)有界タイプの」

関連する問題