我正在尝试为子类列表创建一个泛型方法,无论我选择哪个子类,它都会添加一个新元素。
就此而言,我做了一个容易理解的例子。 有动物园容器的长颈鹿和斑马名单。斑马和长颈鹿都是动物。我需要创造一个“伴侣”。能够获得同性恋基因类型列表的方法能够获得长颈鹿或者斑马的列表(但不是两者),并且如果有超过2只动物,配偶方法将在现有列表中添加另一种类型的动物(不复制)在列表中。
所以为了解决这个问题,我想用反射,因为我无法在java中启动泛型类型(在java中不允许使用新的T()是不允许的)。
所以我创建了第一个元素的实例,并尝试使用animals.add(newAnimal);
将其添加到列表中..但编译器抱怨它时出现以下错误:
Error: java: no suitable method found for add(Animal)
method java.util.Collection.add(capture#1 of ? extends Animal) is not applicable
(argument mismatch; Animal cannot be converted to capture#1 of ? extends Animal)
method java.util.List.add(capture#1 of ? extends Animal) is not applicable
(argument mismatch; Animal cannot be converted to capture#1 of ? extends Animal)
我通过再次使用反射来获取运行时的add方法来解决它,它正在工作(注释中的行),但是我想知道为什么编译器不允许我使用add对于动物,因为我自己找不到答案。
代码:
class Animal implements Comparable<Animal>{
int numLegs;
@Override
public int compareTo(Animal o) {
return this.numLegs-o.numLegs;
}
}
class Giraffe extends Animal{
int neckLength;
}
class Zebra extends Animal{
int numOfStripes;
}
class Zoo{
List<Giraffe> giraffes=new ArrayList<Giraffe>();
List<Zebra> zebras=new ArrayList<Zebra>();
public void printMostAnimals(){
for(Animal a: getMostAnimals()){
System.out.println(a);
}
}
private List<? extends Animal> getMostAnimals() {
if(giraffes.size()>zebras.size())
return giraffes;
return zebras;
}
public void mate(List<? extends Animal>animals) throws IllegalAccessException, InstantiationException {
if(animals.size()>2){
Class<?> firstAnimalInstanceClass=animals.get(0).getClass();
Animal newAnimal=(Animal)firstAnimalInstanceClass.newInstance();
animals.add(newAnimal);
// animals.getClass().getDeclaredMethod("add",Object.class).invoke(animals,newAnimal);
System.out.println("new mate was added");
return;
}
System.out.println("no mate been added");
}
}
class App{
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Zoo z=new Zoo();
z.zebras.add(new Zebra());
z.zebras.add(new Zebra());
z.zebras.add(new Zebra());
z.mate(z.zebras);
System.out.println("new zebra been added?"+(z.zebras.size()>3));
}
}
谢谢,
答案 0 :(得分:2)
因为有人可能会写:
List<Giraffe> giraffes = new ArrayList<>();
List<? extends Animal> animals = giraffes;
animals.add(new Zebra());
for (Giraffe g : giraffes) { // but there is a Zebra in there!
g.eatLeavesFrom(tallTree); // Zebras can't do that!
}
为了在交配时保持类型安全,你可以这样做:
class Animal<M extends Animal<M>> {
M mate;
}
class Giraffe extends Animal<Giraffe> {}
class Zebra extends Animal<Zebra> {}
允许你写:
<A extends Animal<A>> void matingSeason(List<A> animals) {
A x = null;
for (A a : animals) {
if (x != null) {
a.mate = x;
x.mate = a;
x = null;
} else {
x = a;
}
}
}
答案 1 :(得分:1)
您无法将find /tmp/*.log -type f \
-mtime "+$(( ( $(date '+%s') - $(date -d '3 months ago' '+%s') ) / 86400 ))" -delete
以外的任何内容添加到null
。
例如:
List<? extends Animal>
让我们假设它不会引发编译器错误并且我调用 void foo(List<? extends Animal> animals ){
animals.add(new Animal());// compiler error
}
,它会将foo(new ArrayList<Dog>())
添加到Animal
个狗。显然不应该被允许,因此上面的代码将无法编译。
如果这没有引起错误,那么通用编译时安全的全部内容就会消失。
答案 2 :(得分:0)
您不能添加动物&#34; aa &#34;到List<Giraffe>
因为&#34; aa &#34;可能是Zebra
。
当您声明List<? extends Animal>
时,您告诉编译器它是List<Giraffe>
,List<Zebra>
或List<Animal>
。由于它可能是List<Giraffe>
,我们属于上述情况,这意味着您无法添加Animal
。
如果您删除通用,它应该有效,因为您告诉编译器列表中可能有任何:
((List)animals).add(newAnimal);
但是松散了所有类型的安全性。
相反,为什么不这样做:
class AnimalList<AnimalType extends Animal> extends List<AnimalType> {
void mate() {
Class<AnimalType> firstAnimalInstanceClass = animals.get(0).getClass();
AnimalType newAnimal = (AnimalType)firstAnimalInstanceClass.newInstance();
add(newAnimal);
}
}
在您的动物园中,您将声明:
AnimalList<Zebra>
AnimalList<Giraffe>
然后不带参数调用mate
:
AnimalList<? extends Animal> list;
list.mate();