在generics中扩展的超级和默默无闻的有用示例?

我知道关于这个话题有很多问题,但不幸的是他们无法帮助我消除我的晦涩。 首先,看下面的例子。 我不明白,为什么以下“添加”-method someCage.add(rat1)不起作用并中止以下exception:

线程“main”中的exceptionjava.lang.Error:未解决的编译问题:Cage类型中的方法add(capture#2-of?extends Animal)不适用于参数(Rat)

这与Cage不是Cage原因相同吗? 如果是,我在这个例子中不理解它,所以我不确定编译器到底做了什么。 这是代码示例:

 package exe; import cage.Cage; import animals.Animal; import animals.Ape; import animals.Lion; import animals.Rat; public class Main { public static void main(String[] args) { Lion lion1 = new Lion(true, 4, "Lion King", 8); Lion lion2 = new Lion(true, 4, "King of Animals", 9); Ape ape1 = new Ape(true, 2, "Gimpanse", true); Ape ape2 = new Ape(true, 2, "Orang Utan", true); Rat rat1 = new Rat(true, 4, "RatBoy", true); Rat rat2 = new Rat(true, 4, "RatGirl", true); Rat rat3 = new Rat(true, 4, "RatChild", true); Cage animalCage = new Cage(); animalCage.add(rat2); animalCage.add(lion2); Cage ratCage = new Cage(); ratCage.add(rat3); ratCage.add(rat1); ratCage.add(rat2); // ratCage.add(lion1); //Not Possible. A Lion is no rat Cage lionCage = new Cage(); lionCage.add(lion2); lionCage.add(lion1); Cage someCage = new Cage(); //? = "unknown type that is a subtype of Animal, possibly Animal itself" someCage = ratCage; //OK // someCage = animalCage; //OK someCage.add(rat1); //Not Possible, but why? animalCage.showAnimals(); System.out.println("\nRatCage........"); ratCage.showAnimals(); System.out.println("\nLionCage........"); lionCage.showAnimals(); System.out.println("\nSomeCage........"); someCage.showAnimals(); } } 

这是笼子类:

 package cage; import java.util.HashSet; import java.util.Set; import animals.Animal; public class Cage { //A cage for some types of animals private Set cage = new HashSet(); public void add(T animal) { cage.add(animal); } public void showAnimals() { for (T animal : cage) { System.out.println(animal.getName()); } } } 

而且,如果你能用这个动物笼代码给我一个有意义的“超级”例子,我会很高兴的。 到现在为止我还没有理解如何使用它。 有很多理论上的例子,我读到了关于PECS的概念,但无论如何我还没有能够在有意义的事情中使用它。 在这个例子中有一个“消费者”(有超级)意味着什么?

超级绑定示例

引入的transferTo()方法接受Cage Cage – 一个拥有超类T的Cage。 因为T是它的超类的一个实例,所以把一个T放在一个Cage Cage

 public static class Cage { private Set pen = new HashSet(); public void add(T animal) { pen.add(animal); } /* It's OK to put subclasses into a cage of super class */ public void transferTo(Cage cage) { cage.pen.addAll(this.pen); } public void showAnimals() { System.out.println(pen); } } 

现在让我们看看 在行动:

 public static class Animal { public String toString() { return getClass().getSimpleName(); } } public static class Rat extends Animal {} public static class Lion extends Animal {} public static class Cage { /* above */ } public static void main(String[] args) { Cage animals = new Cage(); Cage lions = new Cage(); animals.add(new Rat()); // OK to put a Rat into a Cage lions.add(new Lion()); lions.transferTo(animals); // invoke the super generic method animals.showAnimals(); } 

输出:

 [Rat, Lion] 

另一个重要的概念是,尽管如此:

 Lion instanceof Animal // true 

不是真的

 Cage instanceof Cage // false 

事实并非如此,这段代码会编译:

 Cage animals; Cage lions; animals = lions; // This assignment is not allowed animals.add(rat); // If this executed, we'd have a Rat in a Cage 

您可以将鼠标添加到笼子<鼠标>(当然)。

您可以将鼠添加到笼子<动物>,因为鼠“是”动物(扩展动物)。

你不能将鼠添加到笼子里? 扩展Animal>,因为<? extends Animal>可能是,而Rat则不是。

换一种说法:

 Cage cageA = new Cage(); //perfectly correct, but: cageA.add(new Rat()); // is not, the cage is not guaranteed to be an Animal or Rat cage. // It might as well be a lion cage (as it is). // This is the same example as in Kaj's answer, but the reason is not // that a concrete Cage is assigned. This is something, the // compiler might not know at compile time. It is just that //  cannot guarantee that it is a Cage and // NOT a Cage //You cannot: Cage cageB = new Cage(); //because a "rat cage" is not an "animal cage". //This is where java generics depart from reality. //But you can: Cage cageC = new Cage(); cageC.add(new Rat()); // Because a Rat is an animal. 

想象一下你的凯奇<? 扩展Animal>由抽象工厂方法创建,该方法由子类实现。 在您的抽象基类中,您无法分辨实际分配的类型,编译器也不能,因为具体的类可能只在运行时加载。

那意味着,编译器不能依赖Cage <? 将Animal>扩展为不是某个其他具体子类型的Cage,这将使不同子类型的赋值成为错误。

到目前为止,这两个答案都很棒。 我只是想添加一个花絮来帮助你理解它们。

为了进一步回答Ron的回答,您可能会想到以下内容:

“为什么someCage.add(rat1)成为Cage.add(rat1) ?someCage不能指向任何扩展Animal的任何类型的Cage(我现在将它设置为点一只老鼠的笼子?)“

完全合情合理的问题。 someCage = ratCage是,当你做someCage = ratCage ,逐个元素的复制是从ratCage到someCage完成的。 所以实际上,你并没有简单地将someCage设置为指向ratCage。 实际上,有些Cage仍是Cage Cage 。 你不能做someCage.add(rat1)因为你不知道Cage的类型,只是它的类型被Animal限制在上面。

PS:你不能向someCage添加任何东西,因为它的类型是未知的

我认为你的问题可以通过以下代码片段来回答:

 Cage cage = new Cage(); cage.add(rat1); 

您可以清楚地看到上面的代码不应该是有效的,因为您知道笼子当前是狮笼,并且您不应该允许将鼠添加到狮笼。

编译器没有分配给笼子的值,因此即使您将笼子分配给笼子,它也不能允许cage.add(rat1)