【问题标题】:Useful example with super and obscurity with extends in Generics?在泛型中扩展超级和晦涩的有用示例?
【发布时间】:2011-07-26 07:53:13
【问题描述】:

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

线程“主”java.lang.Error 中的异常:未解决的编译 问题:类型中的方法 add(capture#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 概念的信息,但无论如何我还不能在有意义的事情上使用它。在这个例子中,有一个“消费者”(带有超级用户)意味着什么?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    超级绑定示例

    引入的transferTo() 方法接受Cage&lt;? super T&gt; - 一个包含T 超类的Cage。因为 T 是它的超类的一个实例,所以可以将 T 放在 Cage&lt;? super T&gt; 中。

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

    现在让我们看看&lt;? super T&gt; 的实际效果:

    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>
    

    【讨论】:

    • 很好的例子,谢谢。我现在已经清楚多了。唯一仍然令人困惑的是调用cage.cage。 cage.addAll 不起作用,cage.cage.addAll 起作用,但我不确定如何阅读cage.cage.addAll。这现在是像调用超级方法或其他什么的超级调用吗?
    • 令人困惑的原因是您的示例有一个不幸的字段名称为 cage,这与类名称相同 - Cage 的常用变量名称也是 case。我已更新答案以使用不同的字段名称:pen
    • 好的,我现在几乎完全清楚了,谢谢。但是我唯一不明白的是为什么我不需要getter方法来调用cage.pen.addAll(this.pen)中的“pen”?这是某种反射吗?
    • 因为 Cage 的 pen 对 Cage 的所有(其他)实例可见:private 表示 在类内可见 - 不一定在同一实例内
    【解决方案2】:

    您可以将老鼠添加到笼子(当然)。

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

    您不能将老鼠添加到笼子中,因为 extends Animal> 可能是 ,而 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.
    

    想象一下拥有你的笼子 extends Animal> 由抽象工厂方法创建,该方法由子类实现。在您的抽象基类中,您无法确定实际分配了哪种类型,编译器也无法确定,因为具体类可能仅在运行时加载。

    也就是说,编译器不能依赖 Cage 扩展为不是某个其他具体子类型的 Cage,这会使分配不同子类型成为错误。

    【讨论】:

      【解决方案3】:

      到目前为止,这两个答案都很棒。我只是想补充一点花絮,以帮助您了解它们。

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

      “为什么someCage.add(rat1) 变成了Cage&lt;? extends Animal&gt;.add(rat1)? someCage 不能指向任何类型的任何扩展 Animal 的 Cage(我现在已经将它设置为指向一个老鼠笼子?)”

      完全合法的问题。问题是,当您执行someCage = ratCage 时,会从ratCage 到someCage 中进行逐个元素的复制。所以事实上,你并没有简单地将 someCage 设置为现在指向一个 ratCage。实际上,someCage 仍然是Cage&lt;? extends Animal&gt;。你不能someCage.add(rat1) 因为你不知道Cage 的类型,只是它的类型是由Animal 限定的。

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

      【讨论】:

        【解决方案4】:

        我认为你的问题可以通过下面的sn-p代码来回答:

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

        您可以清楚地看到上面的代码不应该是有效的,因为您知道笼子目前是一个狮子笼子,您不应该被允许在狮子笼子中添加老鼠。

        编译器不知道你给笼子分配了什么值,所以即使你给笼子分配了老鼠笼,它也不能允许cage.add(rat1)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-10-19
          • 2012-09-18
          • 1970-01-01
          • 2010-12-27
          • 2012-08-30
          • 1970-01-01
          • 2016-10-21
          • 1970-01-01
          相关资源
          最近更新 更多