7

我有一个包含 T 类型的一些元素的容器(列表)并且想要过滤它。所以它只包含特定子类型 U 的元素。是否可以设置“动态”返回类型?

例子:

class SomeContainer<T> extends ArrayList<T>{

    public SomeContainer<T> subset(Class c){
        SomeContainer<...here the type of c > output = new SomeContainer<.. also ..>();

        //filter own elements and only add c-objects in the new list

        return output;
    }
}

目前它返回一个泛型类型 T 而不是 c-Class 类型(T 的子类型)的列表。因此我有时会收到以下编译器通知:

Note: SomeContainer.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

因为我想在子类型的对象之后过滤列表并触发特定于子类型的方法,所以我需要一个特定的子类型列表。

4

3 回答 3

9

java.lang.Class是一个自身参数化的泛型类型,因此您可以使用它的类型参数,如下所示:

public <U extends T> SomeContainer<U> subset(Class<U> c){
    SomeContainer<U> output = new SomeContainer<U>();
    for (T val : this) {
        if (c.isInstance(val)) {
            output.add(c.cast(val));
        }
    }
    return output;
}
于 2012-10-14T11:44:18.790 回答
1

泛型只是编译时的产物,因此您的方案无法工作。编译器无法预测在每次执行调用此函数的代码行时您需要什么类。除非您有一个非常受限制且非常无用的情况,即您只使用类文字来调用您的函数,否则您无法使此解决方案类型安全。但是,正如您所说,这几乎肯定会破坏其成为动态的目的。

于 2012-10-14T11:46:59.967 回答
0

第一点:您可以通过将Class c参数替换为Class<T> c. 这可能就是你所需要的......在这种情况下...... =:-)

第二点:通常调用 SomeContainer.subset() 的代码会在编译类型时知道类型 U (来自逻辑上下文)。这一定是你的情况,否则你将无法传递Class c参数。

尝试:

class SomeContainer<T> extends ArrayList<T>{

    public <U extends T> SomeContainer subset(Class<U> c){
        SomeContainer<U> output = new SomeContainer<U>();  
        // put filtered elements into output

        return output;    
    }
}

看看我在那里做了什么?
在方法调用中引入了第二种类型的参数:U extends T. 在与 的论点中也使用了这个Class<U> c
调用者会像这样调用(其中 X 被选择为 T 的子类):

SomeContainer<X> mySubset = mySomeContainer.subset(X.class);     // type inference
SomeContainer<X> mySubset = mySomeContainer.<X>subset(X.class);  // type arg specified 


如果您需要比这更动态的东西,通配符可以提供帮助 - 允许传入和传出参数化类型的“家族”:

public SomeContainer<? extends X> subset(Class<? extends X> c){

这是一个“塑料”函数接口:您可以返回SomeContainer<T>SomeContainer<X>为任何作为 T 子类的 X。以下也适用:

public SomeContainer<? super Z> subset(Class<? extends X> c){

然而,正如另一位发帖人所说,泛型是一种编译时构造,它们在编译期间被生成的非泛型代码替换。这意味着您不能用一行代码动态决定用于实例化泛型类型的类型。但是你可以作弊:如果 T 的子类数量有限,比如 X、Y 和 Z,其中 Z 扩展 Y,Y 扩展 Z,那么你可以使用一个古老的 hacky “if 语句”。尝试:

类 SomeContainer 扩展 ArrayList{

public SomeContainer<? extends X> subset(Class<? extends X> c){
    SomeContainer<? extends X> output = null;

    // would like to use:  "if (c instance of Class<Z>)"
    // but instanceof does not allow generic type arguments
    if (c.getName().equals(Z.class.getName())) {
        SomeContainer<Z> outputZ = new SomeContainer<Z>();
        // put filtered elements into outputZ
        output = outputZ;
    } else if (c.getName().equals(Y.class.getName())) {
        SomeContainer<Y> outputY = new SomeContainer<Y>();
        // put filtered elements into outputZ
        output = outputY;
    } else if (c.getName().equals(X.class.getName())) {
        SomeContainer<X> outputX = new SomeContainer<X>();
        // put filtered elements into outputZ
        output = outputX;
    }
    return output;    
}

}

简单的!(或不)=:-)

于 2012-10-15T05:32:30.130 回答