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

时间:2011-07-26 07:53:13

标签: java generics

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

  

线程“main”中的异常java.lang.Error:未解析的编译   问题:该方法在类型中添加(捕获#2-of?extends Animal)   凯奇不适用于   争论(鼠)

这与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();
    }
}

这是笼子类:

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

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

4 个答案:

答案 0 :(得分:9)

超级绑定的示例

引入的transferTo()方法接受Cage<? super T> - 一个拥有T超类的Cage。因为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);
    }
}

现在让我们看看<? 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();
}

输出:

[Rat, Lion]



另一个重要的概念是,虽然确实如此:

Lion instanceof Animal // true

Cage<Lion> instanceof Cage<Animal> // false

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

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>

答案 1 :(得分:6)

您可以将鼠添加到笼子&lt; Rat&gt; (当然)。

您可以将鼠标添加到Cage&lt; Animal&gt;,因为鼠“是”动物(扩展动物)。

您无法将鼠标添加到笼子中<?扩展Animal&gt;,因为&lt;?延伸动物&gt;可能是&lt; Lion&gt;,而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.

想象一下你的Cage&lt;?延伸动物&gt;由抽象工厂方法创建,由子类实现。在您的抽象基类中,您无法分辨实际分配的类型,编译器也不能,因为具体类可能只在运行时加载。

这意味着,编译器不能依赖Cage&lt;?延伸动物&gt;不是某个其他具体子类型的笼子,这会使不同子类型的赋值成为错误。

答案 2 :(得分:1)

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

为了进一步回答罗恩的回答,你可能会想到以下几点:

“为什么someCage.add(rat1)成为Cage<? extends Animal>.add(rat1)?不能将某些文章指向任何扩展Animal的任何类型的Cage(我现在将其设置为指向一个笼子大鼠?)“

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

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

答案 3 :(得分:0)

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

Cage<? extends Animal> cage = new Cage<Lion>();
cage.add(rat1);  

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

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