我知道关于这个话题有很多问题,但遗憾的是他们无法帮助我消除我的晦涩.首先,看下面的例子.我不明白,为什么以下"添加"-method someCage.add(rat1)不起作用并以下列异常中止:
线程"main"中的异常java.lang.Error:未解决的编译问题:Cage类型中的方法add(捕获#2-of?extends Animal)不适用于参数(Rat)
这Cage<Rat>是不一样的原因Cage<Animal>吗?如果是,我在这个例子中不理解它,所以我不确定编译器到底做了什么.这是代码示例:
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<Animal> animalCage = new Cage<Animal>();
animalCage.add(rat2);
animalCage.add(lion2);
Cage<Rat> ratCage = new Cage<Rat>();
ratCage.add(rat3);
ratCage.add(rat1);
ratCage.add(rat2);
// ratCage.add(lion1); //Not Possible. A Lion is no rat
Cage<Lion> lionCage = new Cage<Lion>();
lionCage.add(lion2);
lionCage.add(lion1);
Cage<? extends Animal> someCage = new Cage<Animal>(); //? = "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();
}
}
Run Code Online (Sandbox Code Playgroud)
这是笼子类:
package cage;
import java.util.HashSet;
import java.util.Set;
import animals.Animal;
public class Cage<T extends Animal> { //A cage for some types of animals
private Set<T> cage = new HashSet<T>();
public void add(T animal) {
cage.add(animal);
}
public void showAnimals() {
for (T animal : cage) {
System.out.println(animal.getName());
}
}
}
Run Code Online (Sandbox Code Playgroud)
此外,如果你能用这个动物笼代码给我一个有意义的"超级"例子,我会很高兴的.到现在为止我还没有理解如何使用它.有很多理论上的例子,我读到了关于PECS的概念,但无论如何我还没有能够在有意义的事情上使用它.在这个例子中有一个"消费者"(有超级)意味着什么?
引入的transferTo()方法接受Cage<? super T>- 一个持有超类的Cage T.因为T是它的超类的一个实例,所以可以放入T一个Cage<? super T>.
public static class Cage<T extends Animal> {
private Set<T> pen = new HashSet<T>();
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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我们看看<? 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<T extends Animal> { /* above */ }
public static void main(String[] args) {
Cage<Animal> animals = new Cage<Animal>();
Cage<Lion> lions = new Cage<Lion>();
animals.add(new Rat()); // OK to put a Rat into a Cage<Animal>
lions.add(new Lion());
lions.transferTo(animals); // invoke the super generic method
animals.showAnimals();
}
Run Code Online (Sandbox Code Playgroud)
输出:
[Rat, Lion]
Run Code Online (Sandbox Code Playgroud)
另一个重要的概念是,尽管如此:
Lion instanceof Animal // true
Run Code Online (Sandbox Code Playgroud)
这不是真的
Cage<Lion> instanceof Cage<Animal> // false
Run Code Online (Sandbox Code Playgroud)
事实并非如此,这段代码会编译:
Cage<Animal> animals;
Cage<Lion> lions;
animals = lions; // This assignment is not allowed
animals.add(rat); // If this executed, we'd have a Rat in a Cage<Lion>
Run Code Online (Sandbox Code Playgroud)
您可以将鼠标添加到笼子<鼠标>(当然).
您可以将鼠添加到Cage <Animal>,因为Rat"是"动物(扩展动物).
你不能将鼠添加到笼子里?扩展Animal>,因为<?extends Animal>可能是<Lion>,而Rat则不是.
换一种说法:
Cage<? extends Animal> cageA = new Cage<Lion>(); //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<Lion> 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<Rat> and
// NOT a Cage<Lion>
//You cannot:
Cage<Animal> cageB = new Cage<Rat>(); //because a "rat cage" is not an "animal cage".
//This is where java generics depart from reality.
//But you can:
Cage<Animal> cageC = new Cage<Animal>();
cageC.add(new Rat()); // Because a Rat is an animal.
Run Code Online (Sandbox Code Playgroud)
想象一下你的凯奇<?扩展Animal>由抽象工厂方法创建,该方法由子类实现.在您的抽象基类中,您无法分辨实际分配的类型,编译器也不能,因为具体的类可能只在运行时加载.
那意味着,编译器不能依赖Cage <?将Animal>扩展为不是某个其他具体子类型的Cage,这会使不同子类型的赋值成为错误.