我认为你所展示的并不是"滥用"。只要不进行任何不安全的转换,所有使用 AbstractFoo
和 C3
的代码仍然是完全类型安全的。在AbstractFoo
中 SELF
的边界意味着代码可以依赖于 SELF
是 AbstractFoo<SELF>
的子类型,但代码不能依赖于 AbstractFoo<SELF>
是 SELF
的子类型。因此,例如,如果 AbstractFoo
有一个返回 SELF
的方法,并且它通过返回 this
来实现(如果它确实是一个“自我类型”应该是可能的),那么它将无法编译:
abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> {
public SELF someMethod() {
return this;
}
}
编译器不允许你编译这个代码,因为它是不安全的。例如,在C3上运行此方法将会返回类型C1的this(实际上是一个C3实例),这将在调用代码中引起类转换异常。如果你试图通过使用强制转换来绕过编译器,例如:
return (SELF)this;
,那么你会得到一个未经检查的强制转换警告,这意味着你需要对它的不安全性负责。
如果你的AbstractFoo只是按照
SELF extends AbstractFoo<SELF>
的要求使用,而不依赖于
AbstractFoo<SELF> extends SELF
的事实,那么你为什么还要关心对C3的“滥用”呢?你仍然可以很好地编写你的类
C1 extends AbstractFoo<C1>
和
C2 extends AbstractFoo<C2>
。如果其他人决定编写一个类
C3 extends AbstractFoo<C1>
,只要他们没有使用不安全的强制转换方式,那么编译器保证它仍然是类型安全的。也许这样的类可能做不了什么有用的事情;我不知道。但它还是安全的,那么这是个问题吗?
递归边界
<SELF extends AbstractFoo<SELF>>
之所以没有被广泛使用,是因为在大多数情况下,它并不比
<SELF>
更有用。例如,Comparable接口的类型参数就没有边界。如果有人决定编写一个类
Foo extends Comparable<Bar>
,他们可以这样做,而且是类型安全的,尽管并不是很有用,因为在大多数使用Comparable的类和方法中,它们都有一个类型变量
<T extends Comparable<? super T>>
,这需要
T
与自身可比较,因此
Foo
类不能作为任何这些位置的类型参数使用。但是如果有人想这样做,编写
Foo extends Comparable<Bar>
仍然是可以的。
递归边界
<SELF extends AbstractFoo<SELF>>
只有在真正利用了
SELF
扩展
AbstractFoo<SELF>
这个事实的地方才会用到,这种情况非常少见。其中一个地方是在构建器模式之类的东西中,它具有返回对象本身的方法,可以链接使用。因此,如果你有这样的方法:
abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> {
public SELF foo() { }
public SELF bar() { }
public SELF baz() { }
}
如果您有一个常规值为AbstractFoo <?> x
,那么您可以做一些像x.foo().bar().baz()
这样的事情,如果它声明为abstract class AbstractFoo<SELF>
则无法实现。
在Java泛型中,没有一种方法可以使类型参数必须与当前实现类的类型相同。假设有这样一种机制,那么这可能会导致继承方面的棘手问题:
abstract class AbstractFoo<SELF must be own type> {
public abstract int compareTo(SELF o);
}
class C1 extends AbstractFoo<C1> {
@Override
public int compareTo(C1 o) {
}
}
class SubC1 extends C1 {
@Override
public int compareTo() {
}
}
这里,SubC1
隐式继承了 AbstractFoo<C1>
,但这破坏了 SELF
必须是实现类的类型的约定。如果 SubC1.compareTo()
必须带一个 C1
参数,那么收到的东西的类型不再与当前对象本身的类型相同。如果 SubC1.compareTo()
可以带一个 SubC1
参数,那么它不再覆盖 C1.compareTo()
,因为它不再像超类中的方法那样具有广泛的参数集。