Java - 通过构造函数将泛型列表传递给类

时间:2014-12-24 04:13:32

标签: java generics inheritance polymorphism

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

说我有

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<? extends Animal> animalList)编译。这有点超过我的java经验,非常感谢一些指导。我希望示例课程足够清楚,以了解我的意图。

编译器告诉我:java.util.List<Animal> in MyAbstractClass cannot be applied to java.util.List<capture<? extends Animal>> 感谢。

编辑: 看起来我必须说出我想要做的事情。我有一个动物列表,可能有或没有比超类更多的字段和方法,但它们都扩展了超类。 我还想要一个类,它接收单一类型动物的列表,并在List中的每个元素上或上方调用方法。我不会在列表中添加任何内容,因此不应该成为问题。 我这样分裂的原因是因为在所有动物之间,有很多相似之处,但有些动物有特色。列入名单的班级需要能够处理这些特殊性。因此,worker类扩展了一个超类并实现了额外的方法。

EDIT2: 工作了!

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<? extends Animal> foobar。 建设性的头脑风暴会议!

EDIT3: 迪马的方法更好。

6 个答案:

答案 0 :(得分:5)

好吧,你不能将List<? extends Animal>分配给List<Animal>,因为列表不是协变的:前者不是后者的子类。

您需要将基类成员的声明(以及getAnimalList()的返回类型)更改为List<? extends Animal>。你在其中一条评论中提到过,这样做会在MyCatClass中给你一些其他的错误,但你必须弄错,如果基类中的所有内容都被正确声明,该类应该没问题(不是{{1} },但是List<Animal>)。

这一行:List<? extends Animal>可能就是导致你麻烦的那一行。首先,你需要围绕蛇的一对括号:(Snake)getAnimalList().get(0).ShedSkin(),其次,你不能将((Snake) getAnimalList().get(0)).ShedSkin()强制转换为List<Animal>,确保声明List<Snake>返回{ {1}},那么一切都应该编译。

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

getAnimalList()

等。 这样您就不需要在任何地方进行投射,因为列表的类型总是准确地知道。

答案 1 :(得分:1)

你必须改变这个:  私人List<Animal> myAnimalList to private List<? extedns Animal> myAnimalList

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

主要原因是Cat扩展了Animal,但List<Cat>没有扩展List<Animal>。 java泛型不允许List<Cat>List<Animal>。 示例:您不能这样做: List<Animal> animalList= new ArrayList<Cat>(); java不允许这样做。否则Snake可以添加animalList哪个含糊不清。 但你可以像这样投射:List<? extends Animal> animalList= new ArrayList<Cat>();在这个投射中你只能调用Animal类的函数。因此,您可以在animalList中添加任何类型的动物。

答案 2 :(得分:1)

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

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

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

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

Animal

除非每个来电者都可以修改动物列表,否则您还应该更改动物列表的访问者:

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

如果动物列表永远不会改变,那么可以使用来自Google的番石榴库的public List<Animal> getAnimalList() return Collections.unmodifiableList(myAnimalList); } 替代解决方案:

ImmutableList

这个解决方案的优点在于:(a)列表确实是不可变的(与Collections.unmodifiableList相比,它只包含一个可变列表)和(b)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 } 足够聪明,知道列表时它receive是ImmutableList.copyOf(),不需要复制。由于(a),ImmutableList可以简单地返回列表。

BTW:方法名称的Java约定是以小写字母开头,例如shedSkin()而不是ShedSkin() - 这是C#约定。

答案 3 :(得分:0)

要使其工作,您必须从:

更改MyAbstractClass中的字段
private List<Animal> myAnimalList;

private List<? extends Animal> myAnimalList;

或将构造函数更改为

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

答案 4 :(得分:0)

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

答案 5 :(得分:-1)

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

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