在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 super T>
Cage super T>
– 一个拥有超类T
的Cage。 因为T是它的超类的一个实例,所以把一个T
放在一个Cage super T>
Cage super T>
。
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 super T> cage) { cage.pen.addAll(this.pen); } public void showAnimals() { System.out.println(pen); } }
现在让我们看看 super T>
super T>
在行动:
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>可能是
换一种说法:
Cage extends Animal> 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 // extends Animal> 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 extends Animal>.add(rat1)
?someCage不能指向任何扩展Animal的任何类型的Cage(我现在将它设置为点一只老鼠的笼子?)“
完全合情合理的问题。 someCage = ratCage
是,当你做someCage = ratCage
,逐个元素的复制是从ratCage到someCage完成的。 所以实际上,你并没有简单地将someCage设置为指向ratCage。 实际上,有些Cage extends Animal>
仍是Cage extends Animal>
Cage extends Animal>
。 你不能做someCage.add(rat1)
因为你不知道Cage的类型,只是它的类型被Animal限制在上面。
PS:你不能向someCage添加任何东西,因为它的类型是未知的
我认为你的问题可以通过以下代码片段来回答:
Cage extends Animal> cage = new Cage(); cage.add(rat1);
您可以清楚地看到上面的代码不应该是有效的,因为您知道笼子当前是狮笼,并且您不应该允许将鼠添加到狮笼。
编译器没有分配给笼子的值,因此即使您将笼子分配给笼子,它也不能允许cage.add(rat1)
。