4

Java のジェネリック クラスに問題があります。

私はこのクラスを持っています:

public abstract class MyMotherClass<C extends AbstractItem> 
{
    private C   item;

    public void setItem(C item)
    {
        this.item = item;
    }

    public C getItem()
    {
        return item;
    }
}

このクラスの実装は次のとおりです。

public class MyChildClass extends MyMotherClass<ConcreteItem>
{

}

ConcreteItem は、AbstractItem (抽象) を拡張する単純なクラスです。

MyChildClass には ConcreteItem があり、次を使用できます。

MyChildClass child = new MyChildClass();
child.setItem(new ConcreteItem());

// automatic cast due to generic class
ConcreteItem item = child.getItem();

わかりました、今のところすべて問題ありません。問題は次のとおりです。

ここで、コレクションから MyMotherClass のインスタンスを抽出し、そのアイテムを設定します (タイプは不明です)。

Map<String, MyMotherClass> myCollection = new HashMap<String, MyMotherClass>();
Map<String, AbstractItem> myItems = new HashMap<String, AbstractItem>();

// fill the 2 collections
...


MyMotherClass child = myCollection.get("key");
child.setItem(myItems.get("key2"));

私がこのようにすれば、それは実行されます。しかし、 MyMotherClass はジェネリック型であり、ジェネリック型を使用していないため、警告があります。しかし、抽出した子のタイプがわからないので、ワイルドカードを使用したいと思います。

Map<String, MyMotherClass<?>> myCollection = new HashMap<String, MyMotherClass<?>>();
Map<String, AbstractItem> myItems = new HashMap<String, AbstractItem>();

// fill the 2 collections
...


MyMotherClass<?> child = myCollection.get("key");
child.setItem(myItems.get("key2"));

そして、ここに問題があります: 次のようなコンパイル エラーが発生しました: The method setItem(capture#1-of ?) in the type MyMotherClass is not applied for the arguments (AbstractItem)

継承されたワイルドカードを使用しようとすると、同じ問題が発生します:

Map<String, MyMotherClass<? extends AbstractItem>> myCollection = new HashMap<String, MyMotherClass<? extends AbstractItem>>();
Map<String, AbstractItem> myItems = new HashMap<String, AbstractItem>();

// fill the 2 collections
...


MyMotherClass<? extends AbstractItem> child = myCollection.get("key");
child.setItem(myItems.get("key2"));

私に何ができる ?

あまり流暢ではない私の英語に感謝し、申し訳ありません;)

4

5 に答える 5

6

操作にはワイルドカードwriteが必要です。super

final Map<String, MyMotherClass<? super AbstractItem>> myCollection =
    new HashMap<String, MyMotherClass<? super AbstractItem>>();

final Map<String, AbstractItem> myItems = 
    new HashMap<String, AbstractItem>();

...

final MyMotherClass<? super AbstractItem> child = 
    myCollection.get("key");
child.setItem(myItems.get("key2"));

extendsワイルドカードはread操作用です。

編集:あなたのコメントに応えて。

まず、ワイルドカードが本当に必要かどうかを評価します。

それでも答えが「はい」の場合は、コレクションを最初により具体的な型に初期化し、次に制限付きのワイルドカードにダウンキャストできます。

final Map<String, MyChildClass> myInitCollection =
    new HashMap<String, MyChildClass>();

final Map<String, ConcreteItem> myInitItems = 
    new HashMap<String, ConcreteItem>();

myInitCollection.put( "key", new MyChildClass( )  );

final MyMotherClass< ConcreteItem> child = 
    myInitCollection.get("key");

child.setItem(myInitItems.get("key2"));

final Map<String, ? extends MyMotherClass< ? extends AbstractItem >>
    myCollection = myInitCollection;

final Map<String, ? extends AbstractItem> myItems = myInitItems; 

ただし、 myCollection を安全に にキャストできないことに注意してMap<String, MyMotherClass< ? extends AbstractItem>>ください。

また、制限されたワイルドカード パラメータと、それらを避けるべき場合については、この記事をお読みください。

于 2009-12-15T17:20:56.527 に答える
2

何かが足りないかもしれませんがAbstractItem、汎用クラスではなく明示的なクラスを使用して、 MyMotherClass クラスで次のことを行ってみませんCか?

public abstract class MyMotherClass<C extends AbstractItem> {

    private AbstractItem item;

    public void setItem(AbstractItem item) {
        this.item = item;
    }

    public AbstractItem getItem() {
        return this.item;
    }

}

この変更だけで、ワイルドカード アプローチを使用できるようになります。

Map<String, MyMotherClass<?>> myCollection = new HashMap<String, MyMotherClass<?>>();
Map<String, AbstractItem> myItems = new HashMap<String, AbstractItem>();

// fill the 2 collections

MyMotherClass<?> child = myCollection.get("key");
child.setItem(myItems.get("key2"));

エラーなし。

もちろん、 では、次のようMyChildClassにオーバーライドできます。MyMotherClass#getItem()

@Override
public ConcreteItem getItem() {
    return (ConcreteItem) super.getItem();
}

正しいクラスが返されていることを確認します。のすべてのサブクラスに対するこの同じアプローチMyMotherClassにより、適切な型を返すことができます。

于 2009-12-15T17:22:01.250 に答える
1

AbstractItem問題は、you get from がyou get frommyItemsの正しい型であるかどうかをコンパイラが知る方法がないことMyMotherClassですmyCollectionConcreteItem2aを aに入れようとしている可能性がありますMyMotherClass<ConcreteItem1>

myCollectionこれを処理する 1 つの方法は、 asを定義することMap<String, MyMotherClass<AbstractItem>>です。AbstractItem次に、取得したオブジェクトのいずれかに anyを入れることができますmyCollectionConcreteItemただし、 fromを取得できなくなりますgetItem()

この場合、Alexander Pogrebnyak の提案が実際に機能するとは思いません。MyMotherClass<AbstractItem>あなたの場合、型パラメーターは拡張する必要がAbstractItemあり、彼はそれを宣言しているため、彼のアイデアは基本的に使用することと同等です。? super AbstractItem両方を満たす唯一のクラスはAbstractItemそれ自体です。

設定方法によっては、オブジェクトを使用して何かを実行できる場合がありClassます。たとえば、マップが保持するアイテムのタイプを表すそれぞれのオブジェクトmyCollectionも含まれている場合、それを使用してアイテムをキャストしたり、文字列に加えてタイプでマップをキーにすることもできます。ClassMyMotherClassmyItems

于 2009-12-15T17:56:21.383 に答える
0

追加: 公式リファレンスへのリンク

Sun によると ( J2SE 5.0 での Generics の使用とプログラミングから)

ワイルドカードには次の 3 種類があります。

  1. 「? extends Type」: タイプ Type のサブタイプのファミリを示します。これは最も便利なワイルドカードです
  2. "? super Type": タイプ Type のスーパータイプのファミリを示します
  3. "?": すべてのタイプまたは任意のタイプのセットを示します

@alexander-pogrebnyak が彼の回答で述べているように、superここで使用する必要があります。

于 2009-12-15T17:37:04.400 に答える
-2

あなたの質問には答えがないので、私の答えを受け入れてください。

アイテムの実際のタイプさえ知らない場合、コンパイラーはどのように知ることができますか? 種類がわからないアイテムを、種類がわからないコンテナに入れようとすると、薄い氷の上を歩いています。

プログラマーがコンパイラーよりも実行時のデータ型に関する知識を持っている場合があります。その場合、コンパイラーを落ち着かせるために、キャストと警告の抑制が必要です。

あなたの場合、データ型さえ知らず、コンパイラが魔法のように型をチェックできることを望みます。それはできません。

于 2009-12-16T01:28:44.860 に答える