我一直在尝试使用泛型和多态。我遇到了一个我无法理解的问题。
说我有
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: 迪马的方法更好。
答案 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
中。以下是两种复制列表的方法,顺便提一下也解决了编译问题(因为Cat
或Snake
的每个列表也是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
可以简单地返回列表。
答案 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);
}
}