我有一个包含各种子类型的对象列表:
List<Dog> dogList = new ArrayList<>();
dogList.add(new Poodle())
dogList.add(new Dalmation());
我想搜索此列表并返回指定类的所有实例:
public <T extends Dog> List<T> getAll(Class<T> dogType){
List<T> returnObjects = new ArrayList<>();
for(T obj : dogList){
if(dogType.instanceOf(obj){
returnObjects.add(dogType.cast(obj));
}
}
return returnObjects;
}
用法:
List<Poodle> poodleList = getAll(Poodle.class);
这是我对仿制药的第一次正确尝试,事情感觉他们有点失控。这是一种体面的做事方式,还是我错过了一个更简单/明显的解决方案?
修改:已更新为使用instanceOf()和cast()以避免未经检查的强制转换
答案 0 :(得分:8)
由于您提到您使用的是Java 8,因此可以使用Stream API:
public static <T extends Dog> List<T> getAll(Class<T> dogType) {
return dogList.stream()
.filter(dogType::isInstance)
.map(dogType::cast)
.collect(toList());
}
此方法将从狗列表中创建Stream
,通过仅保留给定类的实例,转换为给定类并最终将该流收集到List中来过滤它们。
使用isInstance
表示如果您使用Poodle.class
调用该方法,则会保留所有Poodle
或Poodle
子类的狗。如果您只想保留Poodle
的狗,则可以使用Class.equals(other)
代替。
另外,请注意使用cast
方法,没有未经检查的警告(此方法已经处理过它),因为我们之前过滤了正确的元素,所以可以安全地执行此操作:no ClassCastException
可以抛出。
答案 1 :(得分:2)
您必须声明方法的返回类型并检查类的isAssignableFrom()
:
public <T extends Dog> List<T> getAll(Class<T> dogType){
List<T> returnObjects = new ArrayList<>();
for(Dog obj : dogList){
if( dogType.isInstance(obj) ){ //as mentioned in the edit as well as the comments
returnObjects.add(dogType.cast(obj));
}
}
return returnObjects;
}
这样调用List<Poodle> poodleList = getAll(Poodle.class);
应该只返回Poodle
或子类的实例,而getAll(Dog.class)
应该返回所有狗。
一些注意事项:
dogList
只包含狗,您应该在此处使用Dog
类型。否则,请使用该数组/集合的泛型类型(如果有疑问,则为Object
)。isAssignableFrom()
检查最左边的类是否与传递的类或超类型相同,即如果Dog.class.isAssignableFrom( Poodle.class )
,Poodle extends Dog
将为真。如果您只是在完全匹配后使用dogType.equals(obj.getClass())
而不是。 修改:关于dogType.isAssignableFrom(obj.getClass())
的简短说明:如果obj
为空,这可能会导致NPE,因此您必须处理它。另一方面,使用dogType.isInstance(obj)
更容易阅读并且不会受到NPE的影响(因为dogType
不应该为空)。 (参见@Tunaki的回答)