引入
Java的泛型中有一种比较特殊的用法:
class SelfBounded<T extends SelfBounded<T>>{}
网上的其他博客说得不是很详细,所以在这里记录一下。先看一个简单的例子(Thinking In Java 4th Edition P500,做了些修改)
class BasicHolder<T> {
int No;
T element;
void set(T arg) { element = arg; }
T get() { return element; }
void f() {
System.out.println("Subtype "+No);
}
}
比较容易理解,这个类负责维护一个变量,并且提供访问接口。常规用法这里不做说明,不过如下的用法也是正确的:
class Subtype extends BasicHolder<Subtype>
{
public Subtype(int No){ this.No = No; }
}
Subtype只负责维护和自己同类型的变量。比较有趣的是你可以像俄罗斯套娃一样循环的嵌套Subtype:
Subtype a1 = new Subtype();
Subtype a2 = new Subtype();
Subtype a3 = new Subtype(3);
Subtype a4 = new Subtype(4);
a2.set(a1);
a3.set(a2);
a4.set(a3);
a4.get().get().get().f();
这段代码输出为Subtype 1
自限定类型
理解了Subtype的原理以后再看自限定类型逻辑就清晰了许多
class SelfBounded<T extends SelfBounded<T>>
SelfBounded也是一个普通的泛型类,只不过需要满足extends关键字限定。对于任意一个满足此条件的类型type,由于在定义type时type还没有定义完成(听起来像是废话),所以不可能有type的子类先产生。所以实际上这个extends关键字就将其限定在自己本身。即type也为SelfBounded。
因此实际上可以将BasicHolder改成:
class BasicHolder<T extends BasicHolder<T>>
而此时Subtype满足此条件,修改前后的区别为修改后会在编译阶段强制要求所有子类都满足自限定的条件。因此只有如下的子类才能够通过编译:
class A extends SelfBounded<A>{} //自限定的定义
class B extends SelfBounded<A>{} //由于A满足自限定条件,因此B的定义是合法的
class C extends SelfBounded{} //原生类型
应用
在Thinking In Java中给出的自限定类的用途为针对子类对基类型成员函数的重载。这里将书中的进行了简化:
class Basic{
Basic b;
public void set(Basic b){ this.b = b; }
}
class SubType extends Basic{
SubType b;
public void set(SubType b){ this.b = b; }
}
SubType st = new SubType();
st.set(new SubType()); //Ok
st.set(new Basic()); //调用了继承自Basic的set函数
可以看到,SubType无法覆写Basic的set函数,因此一直保留着这个接口。而自限定类型可以解决这个问题,上面的类使用自限定类重写为:
class Basic<T extends Basic<T>>{
T b;
public void set(T b){ this.b = b; }
}
class SubType extends Basic<SubType>{}
不仅代码变得更加简洁,而且此时再执行st.set(new Basic());语句时编译器会报错(Basic无法转化为SubType),因为实际上此时的SubType只有一个set方法,即实现了使用不同的类型对基类的成员重载。