泛型:T扩展MyClass vs. T扩展MyClass <T>

maa*_*asg 15 java generics

这两个声明之间是否存在语义差异,还是仅仅是语法糖?

class C<T extends C> VS class C<T extends C<T>>

背景:我最近回答了关于使用该方法的泛型的问题,C<T extends C>并且同行基于提供了类似的答案C<T extends C<T>>.最后,两种备选方案都提供了相同的结果(在提出的问题的背景下).我仍然对这两种结构之间的差异感到好奇.

是否存在语义差异?如果是这样,每种方法的含义和后果是什么?

And*_*yle 9

当然 - 通常这些"自我类型"用于约束子类型以准确返回自己的类型.考虑以下内容:

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不会是类型安全的与你提出的两个边界的弱,因此代码将无法编译. 即使您明智地定义了所有子类.