这两个声明之间是否存在语义差异,还是仅仅是语法糖?
class C<T extends C> VS class C<T extends C<T>>
背景:我最近回答了关于使用该方法的泛型的问题,C<T extends C>并且同行基于提供了类似的答案C<T extends C<T>>.最后,两种备选方案都提供了相同的结果(在提出的问题的背景下).我仍然对这两种结构之间的差异感到好奇.
是否存在语义差异?如果是这样,每种方法的含义和后果是什么?
当然 - 通常这些"自我类型"用于约束子类型以准确返回自己的类型.考虑以下内容:
public interface Operation {
// This bit isn't very relevant
int operate(int a, int b);
}
public abstract class AbstractOperation<T extends AbstractOperation<T>> {
// Lets assume we might need to copy operations for some reason
public T copy() {
// Some clever logic that you don't want to copy and paste everywhere
}
}
Run Code Online (Sandbox Code Playgroud)
很酷 - 我们有一个父类,它有一个有用的运算符,可以特定于子类.例如,如果我们创建一个AddOperation,它的通用参数是什么?由于"递归"泛型定义,这只能是AddOperation给我们:
public class AddOperation extends AbstractOperation<AddOperation> {
// Methods etc.
}
Run Code Online (Sandbox Code Playgroud)
因此copy()保证该方法返回AddOperation.现在让我们想象一下,我们是愚蠢的,恶意的,创造性的,或者其他什么,并尝试定义这个类:
public class SubtractOperation extends AbstractOperation<AddOperation> {
// Methods etc.
// Because of the generic parameters, copy() will return an AddOperation
}
Run Code Online (Sandbox Code Playgroud)
这将被编译器拒绝,因为泛型类型不在其范围内.这非常重要 - 这意味着在父类中,即使我们不知道具体类型是什么(并且它甚至可能是在编译时不存在的类),该copy()方法将返回一个实例那个子类.
如果你只是简单地使用C<T extends C>,那么这个奇怪的定义SubtractOperation 将是合法的,并且你失去了对T那种情况的保证- 因此减法操作可以将自身复制到添加操作中.
这不仅仅是保护您的类层次结构免受恶意子类的影响,更重要的是它为编译器提供了对所涉及类型的更强保证.如果你copy从另一个类altogther 调用任意Operation一个,你的一个结构保证结果将是同一个类,而另一个将需要转换(并且可能不是正确的转换,如上面的SubtractOperation).
像这样的东西例如:
// This prelude is just to show that you don't even need to know the specific
// subclass for the type-safety argument to be relevant
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);
// Yeah OK, it's ignored after this, but the point was about type-safety! :)
return coll;
}
Run Code Online (Sandbox Code Playgroud)
在第一行分配duplicate到T不会是类型安全的与你提出的两个边界的弱,因此代码将无法编译. 即使您明智地定义了所有子类.
| 归档时间: |
|
| 查看次数: |
2052 次 |
| 最近记录: |