2012-10-14 14 views
7

T型の要素のコンテナ(List)を持っていて、それをフィルタリングしたいのですが。そのため、特定のサブタイプUの要素のみが含まれています。「動的」な戻り値の型を設定することは可能でしょうか?それはCクラス・タイプ(Tのサブタイプ)のジェネリック型Tとしないのリストを返す瞬間Java動的リターンタイプですか?

class SomeContainer<T> extends ArrayList<T>{ 

    public SomeContainer<T> subset(Class c){ 
     SomeContainer<...here the type of c > output = new SomeContainer<.. also ..>(); 

     //filter own elements and only add c-objects in the new list 

     return output; 
    } 
} 

:例

。私は、特定のサブタイプのリストを必要とするサブタイプとトリガサブタイプ特異的な方法のオブジェクトの後にリストをフィルタリングしたいので

Note: SomeContainer.java uses unchecked or unsafe operations. 
Note: Recompile with -Xlint:unchecked for details. 

:したがって、私は時々、次のコンパイラのお知らせを受け取ります。

public <U extends T> SomeContainer<U> subset(Class<U> c){ 
    SomeContainer<U> output = new SomeContainer<U>(); 
    for (T val : this) { 
     if (c.isInstance(val)) { 
      output.add(c.cast(val)); 
     } 
    } 
    return output; 
} 
+2

警告は、「クラス」ではなく「クラス」を使用しているためです。テンプレートパラメータで動的な型を使用できるかどうかはわかりません。 – vainolo

+0

*したがって、特定のサブタイプUの要素のみが含まれています*その特定のサブタイプUを宣言しないのはなぜですか? – m0skit0

答えて

9

java.lang.Classは、自身のジェネリック型パラメータ化されています。コンパイラは、この関数を呼び出すコード行の実行ごとにどのクラスを使用するかを予測できません。には、あなたの関数を呼び出すためにクラスリテラルを使用することが非常に制限されていて、まったく役に立たないケースがなければ、このソリューションのタイプセーフなを作ることはできません。しかし、あなたが述べたように、その目的はダイナミックにほぼ確実に敗北します。

+3

それ以上の場合、 'output.add(c.cast(val))'はチェックされていないキャスト警告を避けるためです。 –

+0

@IanRoberts絶対に!素晴らしい提案をありがとう! – dasblinkenlight

1

ジェネリック医薬品のみコンパイル時のアーチファクトあり、したがって、あなたのスキームが動作することはできません:あなたはこのように、その型パラメータを使用できるように

0

最初の点:Class c引数をClass<T> cに置き換えて、未チェックの操作警告を削除できます。それはあなたが必要とするかもしれません。その場合は... = :-)

第2点: 通常、SomeContainer.subset()を呼び出すコードはコンパイルタイプでU(ロジックコンテキストから)を知っています。 。これは、そうでない場合はあなたがClass c引数を渡すことができないだろう、あなたのためのケースでなければなりません

試してみてください。

class SomeContainer<T> extends ArrayList<T>{ 

    public <U extends T> SomeContainer subset(Class<U> c){ 
     SomeContainer<U> output = new SomeContainer<U>(); 
     // put filtered elements into output 

     return output;  
    } 
} 

は、私はそこに何をしたかを参照してください?
メソッドコールに2番目の型パラメータを導入しました。​​。 Class<U> cの引数でもこれを使用します。
呼び出し側は(XがTのサブクラスを選択した場合)のように呼び出します:

SomeContainer<X> mySubset = mySomeContainer.subset(X.class);  // type inference 
SomeContainer<X> mySubset = mySomeContainer.<X>subset(X.class); // type arg specified 


あなたはこれ以上のダイナミックな何かが必要な場合は、ワイルドカードを支援することができます - parametisedの「家族」を許可します外&に渡されるタイプ:

public SomeContainer<? extends X> subset(Class<? extends X> c){ 

これは「プラスチック」機能インタフェースです:あなたはSomeContainer<T>またはを返すことができます以下は、また働くTのサブクラスである任意のXについて:

public SomeContainer<? super Z> subset(Class<? extends X> c){ 

は、しかし、別のポスターが言ったように、ジェネリック医薬品は、コンパイル時の構文です、彼らは時に生成された非汎用コードにコンパイル置き換えられます。つまり、1行のコードでジェネリック型をインスタンス化するために使用する型を動的に決定することはできません。しかし、あなたはちょっと騙すことができます:Tのサブクラスの数が限られていて、X、Y、Z、ZがY、YがZに拡張されている場合、古き良きハッキーな "if文"を使うことができます。試してみてください:

クラスSomeContainerは、ArrayListの{

public SomeContainer<? extends X> subset(Class<? extends X> c){ 
    SomeContainer<? extends X> output = null; 

    // would like to use: "if (c instance of Class<Z>)" 
    // but instanceof does not allow generic type arguments 
    if (c.getName().equals(Z.class.getName())) { 
     SomeContainer<Z> outputZ = new SomeContainer<Z>(); 
     // put filtered elements into outputZ 
     output = outputZ; 
    } else if (c.getName().equals(Y.class.getName())) { 
     SomeContainer<Y> outputY = new SomeContainer<Y>(); 
     // put filtered elements into outputZ 
     output = outputY; 
    } else if (c.getName().equals(X.class.getName())) { 
     SomeContainer<X> outputX = new SomeContainer<X>(); 
     // put filtered elements into outputZ 
     output = outputX; 
    } 
    return output;  
} 

}

簡単に拡張! (またはそうでない)= :-)

関連する問題