当然 - 这些“自我类型”通常用于限制子类型仅返回其自身类型。考虑以下内容:
public interface Operation {
int operate(int a, int b);
}
public abstract class AbstractOperation<T extends AbstractOperation<T>> {
public T copy() {
}
}
很棒 - 我们有一个带有有用操作符的父类,可以针对子类进行特定设置。例如,如果我们创建一个
AddOperation
,它的通用参数可以是什么?由于“递归”通用定义,这只能是AddOperation,因此得到以下结果:
public class AddOperation extends AbstractOperation<AddOperation> {
// Methods etc.
}
因此,
copy()
方法保证返回一个
AddOperation
。现在让我们想象一下,假设我们有些愚蠢、恶意、有创造力或其他什么原因,尝试定义这个类:
public class SubtractOperation extends AbstractOperation<AddOperation> {
}
由于泛型类型不在其边界内,编译器将拒绝此操作。这很重要-这意味着在父类中,即使我们不知道具体类型是什么(甚至可能是在编译时不存在的类),copy()
方法将返回同一子类的实例。
如果你只使用C<T extends C>
,那么这个奇怪的SubtractOperation
定义将会合法,并且你会失去关于T
是什么的保证-因此减法操作可以将自己复制到加法操作中。
这不是为了保护你的类层次结构免受恶意子类的攻击,而是为了让编译器更加确信所涉及的类型。如果你从另一个类中调用copy
来处理任意Operation
,其中一个形式保证结果将是相同的类,而另一个形式则需要强制转换(并且可能不是正确的转换,就像上面的SubtractOperation
一样)。
例如,像这样:
Set<? extends AbstractOperation> operations = ...;
for (AbstractOperation<?> op : operations) {
duplicate(op);
}
private <T extends AbstractOperation<T>> Collection<T> duplicate(T operation) {
T opCopy = operation.copy();
Collection<T> coll = new HashSet<T>();
coll.add(operation);
coll.add(opCopy);
return coll;
}
< p > 将
duplicate
的第一行分配给
T
,如果您提出的两个边界中较弱的不安全,则该操作将不符合类型安全性,因此代码将无法编译。即使您明智地定义了所有子类。< /p >
C
应该使用某种参数化。在我看来,如果不是为了向后兼容性,这将是一个难以处理的编译器错误。 - Andrzej Doyle