2011-12-09 8 views
2

は、たぶん私は、私はCollectionオブジェクトに対する特定のクラスの配列ですか?

private void render(Collection<Object> rows); 

ように書きメソッドインタフェースを持っている...

を非常に簡単かつ明白な何かを見下ろす午前、私は合格する必要があるオブジェクトは、(列挙型からの)配列です。もちろん

Module[] mods = Module.values(); 
widget.render(mods); 

これは動作しませんが、なぜこれが動作しない:

widget.render(Arrays.asList(mods)) 

+0

'列挙型のCollection'が代わりに' 'EnumSet.allOf(Module.class)を使用するようにします。 – viktor

+0

問題の解決策があれば、実際に投票できるようにコメントに比べて回答を投稿しているはずです。しかし、他の答えはより簡単な解を与える。 –

答えて

6

がにあなたのメソッドのシグネチャを変更してみてください...それはモジュールのコレクションに私の配列を点灯し、モジュールがオブジェクトである:あなたの方法は、あらゆるタイプのCollectionを取ると言っている

private void render(Collection<?> rows); 

このされますその前に、Collectionはその型パラメータとしてObjectを具体的に持つべきであると言っていました。

このようなワイルドカードを使用すると、メソッドに渡されたCollectionの使用方法が制限されます。より詳しいアドバイスが必要な場合は、renderメソッドが何をしているのかを見せてください。

この投稿は、Javaでワイルドカードのコレクションを使用に関して読む価値がある:What is PECS (Producer Extends Consumer Super)?

+1

ああ私は...だから、私はまだ重要な教訓を学んでいないことを意味します:ジェネリックで暗黙的な継承はありません、すなわちコレクションはコレクションのサブタイプではありません...ありがとう – faboolous

+0

javaのすべてのオブジェクトはデフォルトオブジェクトを拡張する必要があります。そのため、Objectパラメータが必要なメソッドに何かを渡すことができます。したがって、オブジェクト自体は実際にオブジェクトが渡される問題ではありませんが、Objectオブジェクトが適切なクラスにキャストされていない場合、特殊メソッドや変数、その他の属性はアクセスできません。 –

+0

キャスト*キャスターなし –

4

Collection<Object>Collection<Module>ではありませんので。ジェネリックスに関する素晴らしいチュートリアルはavailable in PDF versionであり、ジェネリックで作業する際には必ず読んでおく必要があります。

たとえば、Generics and Subtyping部分の4ページで説明しているような場合です。

+0

+1は何を文書で読むべきかを指摘するためのものです。その部分は本物の "もちろん、なぜ私はそれを最初に読むときに考えなかったのですか?" –

+0

レンダリング本体の各オブジェクトをObjectからModuleにキャストした場合、彼のやり方は完全にうまくいくでしょう。 –

+0

私はKhanの応答を受け入れました。詳細に説明する文書 – faboolous

0

あなたはモジュール、例にコレクション内の各オブジェクトをキャストした場合:

if(object instanceof Module) 
{ 
    Module m = (Module)object; 
    //Do stuff here with m 
} 

を次にそれは同様に動作するはずです。そうでなければ、他の2つの答えもうまくいきます。

0

この理由は、Javaがどのようにジェネリックを実装しているかに基づいています。私がそれを説明するために見つけた最良の方法は、配列とジェネリックコレクションを正確に比較することです。

アン配列の例あなたがこれを行うことができますアレイと

Integer[] myInts = {1,2,3,4}; 
Number[] myNumber = myInts; 

しかし、あなたがこれを行うにしようとした場合に何が起こるのでしょうか?

Number[0] = 3.14; //attempt of heap pollution 

この最後の行はうまくコンパイルだろうが、あなたはこのコードを実行した場合、あなたはArrayStoreExceptionを得ることができます。

これは、コンパイラをだますことができますが、ランタイムタイプのシステムをだますことはできないことを意味します。配列はと呼ばれるものなので、これはそうです。です。これは実行時にJavaがこの配列が実際にはNumber[]の参照でアクセスされる整数の配列として実際にインスタンス化されたことを知っていることを意味します。

ご覧のとおり、オブジェクトの実際のタイプが1つあります。もう1つは、アクセスに使用する参照のタイプですか?

Javaのジェネリック

を通報しますさて、Javaのジェネリック型の問題は、型情報がコンパイラによって破棄され、それが実行時に利用できないことです。このプロセスはtype erasureと呼ばれます。 Javaのようにジェネリックスを実装するのには良い理由がありますが、これは長い話です。既存のコードとのバイナリ互換性に関係しています。

ここで重要な点は、実行時に型情報がないため、ヒープ汚染が発生していないことを保証する方法がないことです。例えば

List<Integer> myInts = new ArrayList<Integer>(); 
myInts.add(1); 
myInts.add(2); 

List<Number> myNums = myInts; 
myNums.add(3.14); //heap polution 

Javaコンパイラはコンパイル時にこれをやってからあなたを停止しない場合は方法がないため、実行時の型システムがそれを決定するために、実行時に、どちらかのあなたを停止することはできませんこのリストは整数のみのリストであると考えられていました。 Javaランタイムは、作成されたときに整数リストとして宣言されたので、必要なものをこのリストに入れることができます。

このように、Javaの設計者はあなたがコンパイラをだますことができないようにしました。コンパイラを欺くことができない場合(配列でできるように)、実行時型システムを欺くことはできません。

このように、一般的なタイプはです。です。

明らかに、これは指摘されているように多形性を妨げることになります。解決策は、共分散と反分散と呼ばれるJavaジェネリックの2つの強力な機能を使用する方法を学ぶことです。

共分散共分散あなたが構造から項目を読み取ることができますが、あなたはそれに何かを書き込むことはできませんで

。これらはすべて有効な宣言です。

List<? extends Number> myNums = new ArrayList<Integer>(); 
List<? extends Number> myNums = new ArrayList<Float>() 
List<? extends Number> myNums = new ArrayList<Double>() 

そして、あなたはmyNumsから読み取ることができます:あなたは、実際のリストが含まれているものは何でもことを確認することができますので数を拡張し、すべてのものは、番号は、後に、それは(数にupcastedすることができ

Number n = myNums.get(0); 

、右?)

ただし、共変な構造に何かを入れることはできません。

myNumst.add(45L); 

Javaは実際のオブジェクトの実際のタイプが保証できないため、これは許可されません。これは、Numberを拡張するものであれば何でも構いませんが、コンパイラは確実ではありません。あなたは読むことができますが、書くことはできません。contravarianceで

Contravariance

あなたは反対のことを行うことができます。物事を一般的な構造にすることはできますが、そこから読み出すことはできません。この場合

List<Object> myObjs = new List<Object(); 
myObjs.add("Luke"); 
myObjs.add("Obi-wan"); 

List<? super Number> myNums = myObjs; 
myNums.add(10); 
myNums.add(3.14); 

番号は共通の祖先としてオブジェクトを持っている基本的にあるため、オブジェクトの実際の性質は、オブジェクトのリストである、とcontravarianceを通じて、あなたはそれに数字を置くことができます。したがって、すべての数値はオブジェクトなので、これは有効です。

しかし、あなたが数値を取得すると仮定して、この反変種構造から何かを安全に読み取ることはできません。

Number myNum = myNums.get(0); //compiler-error 

ご覧のとおり、コンパイラがこの行を書くことを許可した場合、実行時にClassCastExceptionが発生します。あなただけの、contravarianceを使用した構造のうち、一般的な値を取るようにしようとするとき、あなただけの構造に一般的な値を入れて、正確なジェネリックを使用する際にこのよう

取得/入れ原理

、共分散を使用両方を行う場合は入力してください。

私が持っている最良の例は、ある種の数字をあるリストから別のリストにコピーする次のものです。これは、このような場合のために働く共変性と反変性の力に

public static void copy(List<? extends Number> source, List<? super Number> destiny) { 
    for(Number number : source) { 
     destiny.add(number); 
    } 
} 

ありがとう:

List<Integer> myInts = asList(1,2,3,4); 
List<Integer> myDoubles = asList(3.14, 6.28); 
List<Object> myObjs = new ArrayList<Object>(); 

copy(myInts, myObjs); 
copy(myDoubles, myObjs); 
関連する問題