【问题标题】:Java - Passing generic lists into class via constructorJava - 通过构造函数将通用列表传递给类
【发布时间】:2015-02-22 05:53:35
【问题描述】:

我一直在尝试使用泛型和多态性。我遇到了一个我无法理解的问题。

说我有

public abstract class Animal {
    private age;
    private weight;

    public Animal(){}

    public void Move(){
        //stuff
    }

    public void Eat(){
        //stuff
    }

    //Getters and setters
}

然后

public Cat extends Animal {
    //Constructors
}

public Snake extends Animal {
    //Constructors
    public void ShedSkin(){
        //stuff
    }
}

我也有自己的课程“MyAbstractClass”。在这里,我似乎无法解决问题。

public abstract class MyAbstractClass {
    private List<Animal> myAnimalList;

    public MyAbstractClass(){}

    public MyAbstractClass(List<? extends Animal> animalList) {
        this.myAnimalList = animalList;
    }

    public List<Animal> getAnimalList() {
        return myAnimalList;
    }

    //Stuff which DOES NOT add to the list
}

我希望能够做到:

public class MyCatClass extends MyAbstractClass {
    public MyCatClass(List<Cat> catList) {
        super(catList);
    }
}

public class MySnakeClass extends MyAbstractClass {
    public MySnakeClass(List<Snake> snakeList) {
        super(snakeList);
    }

    public ShedSkin(){
        (Snake)getAnimalList().get(0).ShedSkin(); //Dont worry about indexing
    }
}

然后

public static void main(string[] args) {
    //Make list of cats
    //Make list of snakes

    MyCatClass cats = new MyCatClass(catList);
    MySnakeClass snakes = new MySnakeClass(snakeList);

    snakes.ShedSkin();
}

这对我不起作用。它无法在public MyAbstractClass(List&lt;? extends Animal&gt; animalList) 编译。这有点超出我的 java 经验,非常感谢一些指导。我希望示例类足够清楚以理解我的意图。

编译器告诉我:java.util.List&lt;Animal&gt; in MyAbstractClass cannot be applied to java.util.List&lt;capture&lt;? extends Animal&gt;&gt; 谢谢。

编辑: 看来我必须说明我要做什么。我有一个动物列表,它们可能有也可能没有比超类更多的字段和方法,但它们都扩展了超类。 我还想要一个接收单一类型动物列表的类,并在列表中的每个元素上或之上调用方法。我不会在列表中添加任何内容,所以这应该不是问题。 我之所以这样分手,是因为所有的动物之间,有很多相似之处,但有些动物有特长。接受 List 的类需要能够处理这些特殊性。因此让工人类扩展一个超类并实现额外的方法。

编辑2: 解决了!

public abstract class MyAbstractClass {
        private List<? extends Animal> myAnimalList;

        public MyAbstractClass(){}

        public MyAbstractClass(List<? extends Animal> animalList) {
            this.myAnimalList = animalList;
        }

        public List<Animal> getAnimalList() {
            return myAnimalList;
        }

        //Stuff which DOES NOT add to the list
    }

字段和构造函数都需要为List&lt;? extends Animal&gt; foobar。 建设性的头脑风暴会议!

编辑3: 迪玛的方法更好。

【问题讨论】:

  • List&lt;? extends Animal&gt;List&lt;Animal&gt; 是不同的东西。第一个可以是List&lt;Cat&gt;,例如,您不能在其中添加Dog。第二个是可以添加任何Animal(猫或狗)的列表。集体歇斯底里!恰到好处tutorial topic.

标签: java generics inheritance polymorphism


【解决方案1】:

好吧,您不能将List&lt;? extends Animal&gt; 分配给List&lt;Animal&gt;,因为列表不是协变的:前者不是后者的子类。

您需要将基类成员的声明(以及getAnimalList()的返回类型)更改为List&lt;? extends Animal&gt;。您在其中一个 cmets 中提到,这样做会在 MyCatClass 中给您带来某种其他错误,但您一定弄错了,如果正确声明了基类中的所有内容(不是 List&lt;Animal&gt;,而是 @ 987654327@)。

这一行:(Snake)getAnimalList().get(0).ShedSkin() 可能是给您带来麻烦的那一行。首先,您需要在蛇周围加上一对括号:((Snake) getAnimalList().get(0)).ShedSkin(),其次,您不能将List&lt;Animal&gt; 转换为List&lt;Snake&gt;,确保声明getAnimalList() 返回List&lt;? extends Animal&gt;,然后一切都应该编译。

我认为,在您的情况下,更好的选择是对基类进行参数化:

public abstract class MyAbstractClass<T extends Animal> {
    private List<T> myAnimalList;
    public MyAbstractClass(List<T> animals) { myAnimalList = animals; }
    public List<T> getAnimalList() { return myAnimalList; }
    //etc.
}

public class MyCatClass extends MyAbstractClass<Cat> {
     public MyCatClass(List<Cat> cats) { super(cats); }
}
public class MySnakeClass extends MyAbstractClass<Snake> {
     public MySnakeClass(List<Snake> snakes) { super(snakes); }
     public ShedSkin() { getAnimalList().get(0).ShedSkin(); }
}

等等。 这样你就不需要在任何地方进行强制转换,因为列表的类型总是准确的。

【讨论】:

  • 谢谢。我会试试这个,很快就会回复你!
  • 感谢这个工作,并且比我做的更好!
  • 是的,“泛型”是 java 中“模板”的名称。虽然,在实现层面它们完全不同(在 java 中,它只是一个语法糖,在运行时都丢失了),但在功能上,从用户的角度来看,它们几乎是等价的。
【解决方案2】:

你必须改变这个: 私人List&lt;Animal&gt; myAnimalList to private List&lt;? extedns Animal&gt; myAnimalList

public List<Animal> getAnimalList() {
        return myAnimalList;
    } to
 public List<?extends Animal> getAnimalList() {
            return myAnimalList;
        }

主要原因是 Cat 扩展了 Animal 但 List&lt;Cat&gt; 不扩展 List&lt;Animal&gt;。 java泛型不允许List&lt;Cat&gt;List&lt;Animal&gt;。 示例:您不能这样做: List&lt;Animal&gt; animalList= new ArrayList&lt;Cat&gt;(); java 不允许这样做。否则 Snake 可以添加不明确的 animalList。 但是你可以这样转换:List&lt;? extends Animal&gt; animalList= new ArrayList&lt;Cat&gt;(); 在这个转换中你只能调用 Animal 类的函数。因此,您可以在 animalList 中添加任何类型的动物。

【讨论】:

    【解决方案3】:

    实际上只有在 MyAbstractClass 的构造函数主体中需要更改的一行代码。基本上,您有两个选择:投射传入列表或制作列表的(防御性)副本。

    如果您只是保留对传入列表的引用,则在您的代码之外对该列表所做的所有更改都将反映在 MyAbstractClass 中。这可能是也可能不是您想要的。相反,您对 MyAbstractClass 中的列表所做的任何更改也会影响代码的调用者。这通常是出乎意料的。如果您打算添加到列表中,则转换传入列表的解决方案是危险的,因为您不知道传递给构造函数的列表元素的实际类型(调用者可能已将列表转换为 List&lt;Cat&gt;即使这真的是一个List&lt;BigCat&gt;)——这不是一种类型安全的操作,但不是被禁止的。在构造函数中进行强制转换也不是类型安全的,所以应该避免它。尽管如此,这里是带有强制转换的构造函数:

    public MyAbstractClass(List<? extends Animal> animalList) {
        this.myAnimalList = (List)animalList;
    }
    

    如果MyAbstractClass 想要控制Animals 的列表,则需要制作该列表的副本。请注意,复制列表不会自动克隆列表的元素,因此对动物的更改仍会显示在 MyAbstractClass 中。这里有两种复制列表的方法,顺便也解决了编译问题(因为CatSnake 的每个列表也是Animal 的列表,创建List&lt;Animal 类型的副本很好 - 仅副本承诺它的所有元素都是 Animal 类型,仅此而已):

    public MyAbstractClass(Collection<? extends Animal> animalList) {
        this.myAnimalList = new ArrayList<>(animalList);
    }
    

    您还应该更改动物列表的访问器,除非每个调用者都可以修改动物列表:

    public List<Animal> getAnimalList() 
        return Collections.unmodifiableList(myAnimalList);
    }
    

    如果动物列表永远不会改变,有一个替代解决方案使用来自 Google 番石榴库的ImmutableList

    public abstract class MyAbstractClass {
       private List<Animal> myAnimalList;
    
        public MyAbstractClass(){
            this(ImmutableList.of());
        }
    
        public MyAbstractClass(Collection<? extends Animal> animalList) {
            this.myAnimalList = ImmutableList.copyOf(animalList);
        }
    
        public List<Animal> getAnimalList() {
            return myAnimalList;
        }
    
        //Stuff which DOES NOT add to the list
    }
    

    这个解决方案的优点在于 (a) 列表确实是不可变的(与简单地包装一个可变列表的 Collections.unmodifiableList 相比)并且 (b) ImmutableList.copyOf() 足够聪明,知道它收到的列表是ImmutableListit 不需要复制。由于 (a),getAnimalList() 可以简单地返回列表。

    顺便说一句:Java 方法名称的约定是以小写开头的,例如 shedSkin() 而不是 ShedSkin() - 这是 C# 约定。

    【讨论】:

      【解决方案4】:

      要实现这一点,您必须将 MyAbstractClass 中的字段从:

      private List<Animal> myAnimalList;
      

      private List<? extends Animal> myAnimalList;
      

      或将构造函数更改为

      public MyAbstractClass(List<Animal> animalList){
          // stuff
      }
      

      【讨论】:

      • 看来如果我将 myAnimalList 字段更改为 private List&lt;? extends Animal&gt; myAnimalList;,我会收到相同的编译器消息。更改构造函数会将错误移至我的猫和蛇类。所以现在在 MySnakeClass 和 MyCatClass 中,我得到:java.util.List&lt;Animal&gt; in MyAbstractClass cannot be applied to java.util.List&lt;Cat&gt;
      • @JohnGeddes 您的代码错误:私人年龄; // 没有字段类型
      • @JohnGeddes 是这个问题吗?
      • 不,这不是问题。这是我正在尝试实现的一个人为的简化示例。
      【解决方案5】:

      向 MyAbstractClass 添加一个通用参数,这样您就可以知道列表中项目的类型。

      【讨论】:

        【解决方案6】:

        我只是用你的代码创建了一个项目。请纠正以下错误。

        1) Import the java.util.List package.
        
        2)  another error I find is this.. How don't know your requirement Cannot suggest the solution.
        
        public MyAbstractClass(List<? extends Animal> animalList) {
                this.myAnimalList = animalList; <==== problem you cannot assign List<? extends Animal> to List<Animal> .. Subtyping is not allowed in generic class.
            }
        

        解决方案之一:

        public MyAbstractClass(List<? extends Animal> animalList) {
        
           myAnimalList= new ArrayList<Animal>();   
        
           for(Animal c: animalList){
                this.myAnimalList.add(c);
           }
        }
        

        【讨论】:

        • 这是一个人为的例子,基于我正在从事的一个更大的项目。让 MyAbstractClass 获取猫列表的问题在于,MySnakeClass 无法将其扩展为我的理解。
        • 我不想在我的抽象类中创建一个新的 ArrayList。不过我现在已经解决了。感谢您的帮助!
        猜你喜欢
        • 2012-03-19
        • 1970-01-01
        • 1970-01-01
        • 2016-05-23
        • 1970-01-01
        • 1970-01-01
        • 2021-06-16
        • 2022-01-12
        • 1970-01-01
        相关资源
        最近更新 更多