问题的起因是在阅读java8的源码中发现BaseStream的申明

1
public interface BaseStream<T, S extends BaseStream<T, S>> {...}

其中S extends BaseStream<T, S> 相当隐晦,不知道啥深意。Google之后豁然开朗,又涨姿势。

要理解上面这段代码可以先从简单的情景出发,先要理解它想达到什么效果。比如说我们有下面的两个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Foo {
Foo doSomething() {
// ...
return this;
}
}
public class Bar extends Foo {
Bar doOtherthing() {
// ...
return this;
}
}

如果想实现链式调用,new Bar().doSomething().doOtherthing()会报编译错误,因为doSomething返回的是Foo对象而不含有doOtherthing这个成员函数。解决方法是继承重写:

1
2
3
4
5
6
7
8
9
10
11
12
public class Bar extends Foo {
Bar doOtherthing() {
// ...
return this;
}
@Override
Bar doSomething() {
super.doSomething();
return this;
}
}

然而,如果Foo有很多个链式函数,那Bar也必须全部重写,这显然不好。可以改写下Foo,让doSomething返回根据模板参数确定的类型T,然后Bar继承Foo。这样doSomething知道它应该返回什么类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Foo<T> {
Foo doSomething() {
// ...
return (T) this;
}
}
public class Bar extends Foo<Bar> {
Bar doOtherthing() {
// ...
return this;
}
}

但是这么做没有检查T的类型,T应该必须是Foo的子类,而不能是任意的类型。可以给它加上类型限制:

1
2
3
4
5
6
public class Foo<T extends Foo<T>> {
Foo doSomething() {
// ...
return (T) this;
}
}

注意到T extends Foo<T>Bar extends Foo<Bar> 形式是一致的,所以Bar满足了要求。
这个例子其实就是BaseStreamS extends BaseStream<T, S>的简化(去除了T类型)。总结来说
那段代码是为了约束S必须是BaseStream的子类。

这种模式很早就有人发现了,称为Curiously recurring template pattern 除了在链式调用中的应用外,还有其他有意思的应用,这里就不展开说了。