简体   繁体   English

Java 编译错误 generics

[英]Java compile error with generics

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;
    }
}

Getting a compiler error:获取编译器错误:

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

I understand that I can't convert an impl back to its API interface (because an API is just than - an API).我知道我无法将 impl 转换回其 API 接口(因为 API 只是一个 API)。 But I'm confused as to why I'm getting a compiler error!但是我对为什么会出现编译器错误感到困惑! Shouldn't Java honor the fact that SedimentaryMineral is an impl of IMineral and allow this??? Java 不应该尊重SedimentaryMineralIMineral的一个事实并允许这样做吗???

Along with an explanation as to why I'm getting this compiler error, perhaps someone could point out why my approach here is "bad design" and what I should do to correct it.除了解释为什么我会收到此编译器错误外,也许有人会指出为什么我在这里的方法是“糟糕的设计”以及我应该如何纠正它。 Thanks in advance!提前致谢!

Imagine if this compiled:想象一下,如果编译:

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?
}

You have a serious issue there.你那里有一个严重的问题。

What you can do is this: List<? extends IMineral> mienralList = list你可以做的是: List<? extends IMineral> mienralList = list List<? extends IMineral> mienralList = list

The problem is that Java generics are not covariant ;问题是 Java generics 不是协变的 List<SedimentaryMineral> does not extend/implement List<IMineral> . List<SedimentaryMineral>不扩展/实现List<IMineral>

The solution depends on precisely what you wish to do here.解决方案恰恰取决于您希望在此处执行的操作。 One solution would involve wildcards , but they impose certain limitations.一种解决方案涉及wildcards ,但它们施加了某些限制。

Here is what will work for you:以下是适合您的方法:

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;
    }
}

Here I am using wildcard to denote that I allow list of everything that extends the basic interface to be returned from getMinerals .在这里,我使用通配符表示我允许从getMinerals返回扩展基本接口的所有内容的列表。 Note that I also changed some of your classes to interfaces so that everything will compile (I also removed the accessors of the classes so that I can put them in a single file, but you can add them back).请注意,我还将您的一些类更改为接口,以便所有内容都可以编译(我还删除了类的访问器,以便我可以将它们放在一个文件中,但您可以将它们添加回去)。

First, your code would work if you done something like首先,如果你做了类似的事情,你的代码就会工作

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

Second, you can't do this directly because you wouldn't be able to guarantee type safety from what you insert inside your list.其次,您不能直接执行此操作,因为您无法保证您在列表中插入的内容的类型安全。 So, if you want anything that could extend Mineral inside your rock, do what I showed above.所以,如果你想要任何可以在你的岩石中扩展 Mineral 的东西,请按照我上面显示的进行操作。 If you want that only a specific type of be inserted inside a rock, do something like如果您只想将特定类型的插入到岩石中,请执行类似的操作

public interface IRock<M extends IMineral> {
    public List<M> getMinerals();
}
public class SedimentaryRock implements IRock<SedimentaryMineral> {
   public List<SedimentaryMineral> getMinerals()
   {
    return minerals;
   }
}

You need to understand why this cannot work in general, and why it is a good thing to have the compiler complain here.您需要了解为什么这通常无法工作,以及为什么让编译器在这里抱怨是一件好事。

Assuming we have a class ParkingLot implements Collection<Cars> and since Car extends Vehicle , this would automatically make the ParkingLot also implement Collection<Vehicle> .假设我们有一个 class ParkingLot implements Collection<Cars>并且由于Car extends Vehicle ,这将自动使ParkingLot也实现Collection<Vehicle> Then I could put my Submarine into the ParkingLot .然后我就可以把我的Submarine放到ParkingLot了。

Less funny, but simpler speaking: a collection of apples is not a collection of fruit.不那么有趣,但更简单地说:苹果的集合不是水果的集合。 A collection of fruit may contain bananas, while a collection of apples may not.一组水果可能包含香蕉,而一组苹果可能不包含。

There is a way out of this: using wildcards.有一种解决方法:使用通配符。 A collection of apples is a collection of "a particular subtype of fruit".苹果的集合是“水果的特定子类型”的集合。 By forgetting which kind of fruit it was, you get what you intended: you know it's some kind of fruit you get out.通过忘记它是哪种水果,你得到了你想要的:你知道它是你得到的某种水果。 At the same time, you can't be sure that you are allowed to put in arbitrary fruit.同时,您也不能确定是否允许您随意放入水果。

In java, this is在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

Let me emphasize the difference again:让我再次强调一下区别:

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

Must be able to store any fruit, apples and bananas.必须能够存放任何水果、苹果和香蕉。

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.

Could be a collection of apples, a collection of banananas, a collection of green apples only, or a collection of arbitary fruit.可以是苹果的集合、香蕉的集合、仅青苹果的集合或任意水果的集合。 You don't know which type of fruit, could be a mix.你不知道是哪种水果,可能是混合的。 But they're all Fruit.但他们都是水果。

In read-only situations, I clearly recommend using the second approach, as it allows both specialized ("bag of apples only") and broad collections ("bag of mixed fruit")在只读情况下,我明确建议使用第二种方法,因为它允许专门的(“仅一袋苹果”)和广泛的 collections(“一袋混合水果”)

Key to understanding this is to read Collection<A> as Collection of different kind of A , while Collection<? extends A>理解这一点的关键是将Collection<A>读作不同种类 A 的Collection,而Collection<? extends A> Collection<? extends A> is a Collection of some subtype of A (the exact type however may vary). Collection<? extends A>是 A的某些子类型的 Collection (但确切类型可能会有所不同)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM