现在我已经创建了一个LivingThing
类
public class LivingThing<T extends LivingThingTypes> implements Iterable<LivingThingTypes>{
public ArrayList<T> create(Class<T> type) {
ArrayList arrayList = new ArrayList<>();
for (T item : list) {
if (item.getClass().equals(type)) {
arrayList.add(item);
}
}
return arrayList;
}
}
因此,当我尝试访问类似本节代码的create()
方法时,它将无法正常工作。
livingList.create(Human.class).size();
livingList.create(Cat.class).size();
livingList.create(Hen.class).size();
但是,如果我将方法更改为此,则效果很好
public class LivingThing<T extends LivingThingTypes> implements Iterable<LivingThingTypes>{
public <V extends LivingThingTypes>ArrayList<V> create(Class<V> type) {
ArrayList arrayList = new ArrayList<>();
for (T item : list) {
if (item.getClass().equals(type)) {
arrayList.add(item);
}
}
return arrayList;
}
}
答案 0 :(得分:1)
关于泛型的重要一点是它们是不变的。在您的情况下,这意味着即使Human类扩展了LivingThingTypes
,但List<Human>
却没有扩展List<LivingThingTypes>
。因此,基本上您不能执行以下任务:
List<Human> list1;
List<LivingThingTypes> list2 = list1; //compile error
这就是泛型的设计方式。原因是想像您有一个Animal父类,而Dog和Cat是子类。现在,您创建了一个List<Dog>
并将其分配给List<Animal>
。现在,由于Animal是Cat的父类,因此您也可以将猫添加到List<Animal>
中。反复迭代可能会得到ClassCastException
。现在,使用泛型的基本优点是它们可以提供类型安全性,但是获取ClassCastException
意味着情况并非如此,因此泛型是不变的。
但是,当您将extends与Type参数一起使用时,它允许分配,但是您必须记住PECS(Producer Extends and Consumer Super)。
在第一种情况下,您需要在类级别声明类型参数。现在,当您创建LivingThing类的实例时,必须提及类型参数,例如LivingThing<LivingThingTypes> livingList = new LivingThing();
。这就是T的值。现在,由于我在前面的段落中提到的原因,编译器将期望您在create方法内发送相同的Type。
在第二种情况下,您正在创建一个新的Type参数,该参数仅绑定到该方法,在这里您告诉它create方法的V extends LivingThingType
类型Class<V>
类型参数现在可以接受{{ 1}}。您也可以使用相同的Type参数T来代替V,但为避免混淆,应使用与以往不同的名称。
答案 1 :(得分:0)