0
public class IRock
{
    public List<IMineral> getMinerals();
}

public class IMineral { ... }

public class SedimentaryMineral implements IMineral { ... }

public class SedimentaryRock implements IRock
{
    private List<SedimentaryMineral> minerals;

    @Override
    public List<SedimentaryMineral> getMinerals()
    {
        return minerals;
    }
}

コンパイラ エラーの取得:

Type mismatch: cannot convert from List<SedimentaryMineral> to List<IMineral>.

impl をその API インターフェイスに変換できないことは理解しています (API は単なる API ではないため)。しかし、なぜコンパイルエラーが発生するのか混乱しています! SedimentaryMineralJavaは impl であるという事実を尊重し、IMineralこれを許可するべきではありませんか?!?

なぜこのコンパイラ エラーが発生するのかについての説明とともに、おそらく誰かが、ここでの私のアプローチが「悪い設計」である理由と、それを修正するために何をすべきかを指摘することができます。前もって感謝します!

4

5 に答える 5

6

これがコンパイルされたと想像してください:

List<SedementaryMineral> list = new ArrayList<>();
list.put(new SedimentaryMineral());

List<IMineral> mineralList = list;
mineralList.add(new NonSedimentaryMineral());

for(SedementaryMineral m : list) {
    System.out.println(m); // what happens when it gets to the NonSedimentaryMineral?
}

そこに深刻な問題があります。

あなたができることはこれです:List<? extends IMineral> mienralList = list

于 2012-04-09T15:50:25.593 に答える
2

問題は、Java ジェネリックが共変でないことです。List<SedimentaryMineral>拡張/実装しませんList<IMineral>

解決策は、ここで何をしたいかによって異なります。解決策の 1 つはワイルドカードを使用することですが、特定の制限があります。

于 2012-04-09T15:49:54.617 に答える
1

これがあなたのために働くものです:

interface IRock
{
    public List<? extends IMineral> getMinerals();
}

interface IMineral { }

class SedimentaryMineral implements IMineral {  }

class SedimentaryRock implements IRock
{
    private List<SedimentaryMineral> minerals;

    public List<? extends IMineral> getMinerals()
    {
        return minerals;
    }
}

ここでは、ワイルドカードを使用して、基本インターフェースを拡張するすべてのリストを から返すことを許可することを示していますgetMinerals。すべてがコンパイルされるように、一部のクラスをインターフェイスに変更したことにも注意してください (クラスのアクセサーも削除して、単一のファイルに配置できるようにしましたが、元に戻すことができます)。

于 2012-04-09T15:51:30.253 に答える
0

まず、次のようなことをした場合、コードは機能します

...
public interface IRock
{
    public List<? extends IMineral> getMinerals();
}
...

第二に、リスト内に挿入したものから型の安全性を保証できないため、これを直接行うことはできません。したがって、岩の内部にミネラルを拡張できるものが必要な場合は、上で示したようにしてください. 特定のタイプのみを岩の中に挿入したい場合は、次のようにします

public interface IRock<M extends IMineral> {
    public List<M> getMinerals();
}
public class SedimentaryRock implements IRock<SedimentaryMineral> {
   public List<SedimentaryMineral> getMinerals()
   {
    return minerals;
   }
}
于 2012-04-09T15:59:47.580 に答える
0

これが一般的に機能しない理由と、ここでコンパイラに文句を言わせるのがなぜ良いことなのかを理解する必要があります。

ParkingLot implements Collection<Cars>クラスand sinceがあると仮定するとCar extends Vehicle、これにより自動的にParkingLotも実装されCollection<Vehicle>ます。それから私は私をに入れることができSubmarineましたParkingLot

面白くはありませんが、簡単に言えば、リンゴのコレクションは果物のコレクションではありません。果物のコレクションにはバナナが含まれる場合がありますが、リンゴのコレクションには含まれない場合があります。

これを回避する方法があります: ワイルドカードを使用することです。リンゴのコレクションは、「果物の特定のサブタイプ」のコレクションです。それがどの種類の果物であったかを忘れることで、意図したとおりになります。つまり、それが何らかの種類の果物であることがわかります。同時に、任意の果物を入れることが許可されているかどうかもわかりません。

Javaでは、これは

Collection<? extends Fruit> collectionOfFruit = bagOfApples;
// Valid, as the return is of type "? extends Fruit"
Fruit something = collectionOfFruit.iterator().next();
// Not valid, as it could be the wrong kind of fruit:
collectionOfFruit.put(new Banana());
// To properly insert, insert into the bag of apples,
// Or use a collection of *arbitrary* fruit

違いをもう一度強調しましょう。

Collection<Fruit> collection_of_arbitrary_fruit = ...;
collection_of_arbitrary_fruit.put(new Apple());
collection_of_arbitrary_fruit.put(new Banana());

果物、リンゴ、バナナを保管できる必要があります。

Collection<? extends Fruit> collection_of_one_unknown_kind_of_fruit = ...;
// NO SAFE WAY OF ADDING OBJECTS, as we don't know the type
// But if you want to just *get* Fruit, this is the way to go.

リンゴのコレクション、バナナナのコレクション、青リンゴのみのコレクション、または任意の果物のコレクションである可能性があります。どの種類の果物かはわかりませんが、ミックスである可能性があります。しかし、それらはすべてフルーツです。

読み取り専用の状況では、2 番目のアプローチを使用することを強くお勧めします。これは、特化したコレクション (「りんごのみの袋」) と幅広いコレクション (「ミックス フルーツの袋」) の両方が可能になるためです。

これを理解するための鍵は、異なる種類の A のコレクションCollection<A>として読み取ることですが、はA の何らかのサブタイプのコレクションです(ただし、正確なタイプは異なる場合があります)。Collection<? extends A>

于 2012-04-09T16:03:26.630 に答える