我有一个类型为T的元素的容器(List),想要过滤它。所以它只包含特定子类型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.
因为我想在子类型的对象之后过滤列表并触发特定于子类型的方法,所以我需要一个特定的子类型列表。
答案 0 :(得分: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;
}
答案 1 :(得分:1)
泛型只是一个编译时的人工制品,因此你的方案无法工作。编译器无法预测在每次执行调用此函数的代码行时您想要的类。你不能使这个解决方案类型安全除非你有一个非常有约束力且无用的情况,你只使用类文字来调用你的函数。然而,正如你所说的那样,这几乎肯定会破坏它的目的。动态。
答案 2 :(得分: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
如果你需要比这更动态的东西,通配符可以提供帮助 - 允许参数类型的“家族”传递到&amp;出:
public SomeContainer<? extends X> subset(Class<? extends X> c){
这是一个“塑料”函数接口:您可以为任何X作为T的子类返回SomeContainer<T>
或SomeContainer<X>
。以下也适用:
public SomeContainer<? super Z> subset(Class<? extends X> c){
然而,正如另一张海报所说,泛型是一个编译时构造,它们在编译期间被替换为生成的非泛型代码。这意味着您无法动态决定用于使用单行代码实例化泛型类型的类型。但你可以作弊:如果你有一个有限数量的T的子类,比如X,Y和Z,其中Z延伸Y,Y延伸Z,那么你可以使用一个古老的hacky“if语句”。尝试:
class 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;
}
}
轻松! (或不)=: - )