Java - 返回指定泛型类型的列表

4

我有一个对象列表,其中包含各种子类型:

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()来避免未经检查的转换。


请查看关键字 instanceof - jhamon
for(T obj : dogList) 无法编译:dogList不能是List<T>,因为它在getAll之外声明。你应该在_foreach_中将T替换为Dog - Didier L
顺便说一句,这可能更适合于[codereview.se]... - Didier L
请注意,dogType.instanceOf(obj) 应该改为 dogType.isInstance(obj) - Thomas
2个回答

9

由于您提到使用的是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,通过仅保留那些是给定类的实例并转换成给定类来过滤它们,最终将该Stream收集到一个列表中。

使用 isInstance 意味着如果您使用 Poodle.class 调用该方法,则所有既是 Poodle 或子类的狗将被保留。如果您只想保留 Poodle 狗,则可以使用 Class.equals(other)

此外,请注意使用 cast 方法,没有未经检查的警告(此方法已处理它),在此处执行它是安全的,因为我们在之前过滤了正确的元素:不会抛出任何 ClassCastException 异常。


哦,是的。我完全忘记了 isInstance() ,它比 isAssignableFrom() 更好,因为可以预防 NPE。 (+1) - Thomas
这看起来非常不错。我还没有研究过Stream API,我会把它提高到我的列表中。 - ms813

4

你需要为这个方法声明返回类型并在类上检查 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的答案)


缺少返回类型是一个笔误,我已经在编辑中添加了它。 - ms813
我觉得我不太明白isAssignableFrom和equals之间的区别 - 如果我使用isAssignableFrom,我是否也会得到Poodle的任何子类,但是使用equals我只会得到Poodle? - ms813
@ms813 是的,让我们换一种说法:假设 MiniaturePoodle 扩展了 Poodle。如果您使用 equals,您只会得到 Poodle 的实例,如果您使用 isAssignableFrom,您将得到 PoodleMiniaturePoodle 的实例。 - Thomas
谢谢,我会记住这个建议,继续开发时我还不确定我想要哪种情况! - ms813
1
你应该使用isInstance而不是isAssignableFrom,因为你正在处理实例。这样更易于阅读和理解。此外,您可以使用dogType.cast(obj)来避免未经检查的转换。我认为最好改变代码而不仅仅在编辑中提到它。 - Didier L
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接