填写列表<?扩展 Foo>

Filling a List<? extends Foo>

拥有一组抽象对象:Set<Foo> foes;

我想要这样的方法:

List<? extends Foo> getFoesByType(TypeEnum type);

我试过:

List<? extends Foo> result = new ArrayList<>();
for(Foo f : foes) {
        if(f.getType() == type) {
            switch(type) {
            case TYPE1:
                f = (FooType1) f;
                break;
            case TYPE2:
                /*...*/
            }

            result.add(f);  
            /*The method add(capture#1-of ?) in the type 
            List<capture#1-of ?> is not applicable for the arguments (Foo)*/
        } 
}  

return result;

但是我得到一个错误。

我希望能够做到这一点:List<FooType1> foesType1 = getFooesByType(TypeEnum.TYPE1);哪种方法是正确的?

你不能这样做。 ? extends Foo 表示可能有 Foo class 中的任意 child。你不知道它到底是什么,这就是你不能添加到 List<? extends Foo>.

的原因

但您可以创建数组列表并将其分配给 List,然后 return。

List<? extends Foo> getFoesByType(TypeEnum type) {
    List<Foo> result = new ArrayList<>();
    // add something to result list
    return result;
}

您需要使您的方法通用:

private <T extends Foo> List<T> getFooesByType(EnumType type) {
    List<T> result = new ArrayList<>();
    for(Foo f : foes) {
        if(f.getType() == type) {
            switch(type) {
            case TYPE1:
                // do something, cast is not necessary
                break;
            case TYPE2:
                /*...*/
            }

            result.add((T) f); // cast here
        }
    }

    return result;
}

唯一安全的方法是添加一个Class参数来支持运行时的类型检查:

private <T extends Foo> List<T> getFooesByType(EnumType type, Class<T> type) {
    List<T> result = new ArrayList<>();
    for(Foo f : foes) {
        if(f.getType() == type) {
            switch(type) {
                case TYPE1:
                    // do something, cast is not necessary
                    break;
                case TYPE2:
                    /*...*/
            }
            result.add(type.cast(f)); // safe cast
        }
    }
    return result;
}

被称为

List<FooType1> foesType1 = getFooesByType(TypeEnum.TYPE1, FooType1.class);

这是确保列表元素属于调用方指定类型的唯一方法。另一方面,这使得 TFoo 的子类型的要求已过时。

你可以把<T extends Foo>改成<T>提高方法的灵活性,
例如List<Object> list = getFooesByType(TYPE1, Object.class); 也是合法的。