我一直在玩泛型和多态。我遇到了一个问题,我想不起来了。
说我有
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>>谢谢。
编辑:看来我得把我想要做的事情说出来。我有一个动物列表,这些动物可能比超类有更多的字段和方法,也可能没有,但它们都扩展了超类。我还想要一个类,它接受一个单一类型动物的列表,并对列表中的每个元素调用方法。我不会在列表中添加任何内容,这样就不会有问题了。我之所以这样分手,是因为动物之间有很多相似之处,但有些动物有自己的特点。接受列表的班级需要能够处理这些特殊性。因此,让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:迪马的方法更好。
发布于 2014-12-24 04:55:20
您不能将List<? extends Animal>分配给List<Animal>,因为列表不是协变量的:前者不是后者的子类。
您需要将基类成员的声明(以及getAnimalList()的返回类型)更改为List<? extends Animal>。您在其中一个注释中提到,这样做会在MyCatClass中给出某种其他错误,但您肯定弄错了,如果基类中的所有内容都声明正确(不是List<Animal>,而是List<? extends Animal>),那么该类应该是可以的。
这句话:(Snake)getAnimalList().get(0).ShedSkin()可能是给你带来麻烦的那个人。首先,您需要在蛇周围加上一对括号:((Snake) getAnimalList().get(0)).ShedSkin(),其次,您不能将List<Animal>转换为List<Snake>,请确保声明了getAnimalList()以返回List<? extends Animal>,然后所有内容都应该编译。
我认为,在您的情况下,另一个更好的选择是将基类参数化:
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(); }
}这样你就不需要在任何地方进行铸造了,因为列表的类型总是很清楚的。
发布于 2014-12-24 05:16:42
您必须改变这一点:私有List<Animal> myAnimalList to private List<? extedns Animal> myAnimalList
和
public List<Animal> getAnimalList() {
return myAnimalList;
} to
public List<?extends Animal> getAnimalList() {
return myAnimalList;
}主要原因是猫扩展了动物,而List<Cat>没有扩展List<Animal>。java泛型不允许List<Cat>到List<Animal>。示例:您不能这样做:List<Animal> animalList= new ArrayList<Cat>(); java不允许这样做。因为否则蛇可以添加animalList,其中含糊不清。但是您可以像这样进行转换:List<? extends Animal> animalList= new ArrayList<Cat>();,在这个转换中,您只能调用动物类的函数。因此,您可以在animalList中添加任何类型的动物。
发布于 2014-12-24 05:34:51
实际上,MyAbstractClass构造函数正文中只有一行代码需要更改。基本上,您有两个选项:转换传入的列表或制作列表的(防御)副本。
如果您只保留对传入列表的引用,那么在代码之外对该列表所做的所有更改都将反映在MyAbstractClass中。这可能是你想要的,也可能不是你想要的。相反,对MyAbstractClass中的列表所做的任何更改也会影响代码的调用方。这通常是相当出乎意料的。如果要添加到列表中,转换传入列表的解决方案是危险的,因为您不知道传递给构造函数的列表元素的实际类型(调用方可能已经将列表强制转换到List<Cat>,即使它实际上是一个List<BigCat>) --这不是类型安全操作,但不是禁止的。构造函数中的类型转换也不安全,因此应该避免。不过,下面是具有强制转换的构造函数:
public MyAbstractClass(List<? extends Animal> animalList) {
this.myAnimalList = (List)animalList;
}如果MyAbstractClass想要控制Animal的列表,则需要制作列表的副本。请注意,复制列表并不会自动克隆列表的元素,因此对动物的更改仍然会出现在MyAbstractClass中。这里有两种复制列表的方法,顺便说一句,它也解决了编译问题(因为Cat或Snake的每个列表也都是Animal的列表,创建List<Animal类型的副本是可以的--副本只保证其所有元素都是Animal类型的,仅此而已):
public MyAbstractClass(Collection<? extends Animal> animalList) {
this.myAnimalList = new ArrayList<>(animalList);
}您还应该更改动物列表的访问器,除非每个调用方都可以修改动物列表:
public List<Animal> getAnimalList()
return Collections.unmodifiableList(myAnimalList);
}如果动物列表从未改变,那么还有一种替代解决方案,使用谷歌番石榴库中的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()足够聪明,可以知道当它接收到的列表是ImmutableList时,它不需要被复制。由于(a),getAnimalList()可以简单地返回列表。
顺便说一句:方法名称的Java约定是以小写开始,例如shedSkin(),而不是ShedSkin(),后者是C#约定。
https://stackoverflow.com/questions/27631374
复制相似问题