在grepcode上浏览java 8的流实现时,我在第772行的java.util.stream.ReduceOps.java
中发现了以下声明:
private static final class ReduceTask<P_IN, P_OUT, R,
S extends AccumulatingSink<P_OUT, R, S>>
extends AbstractTask<P_IN, P_OUT, S, ReduceTask<P_IN, P_OUT, R, S>> {
似乎类ReduceTask
正在扩展或更好地引用自身。这真的有可能吗?
答案 0 :(得分:2)
这里发生的是AbstractTask
的第四个类型参数(K)本质上是自我类型。 AbstractTask
的声明如下:
abstract class AbstractTask<P_IN, P_OUT, R,
K extends AbstractTask<P_IN, P_OUT, R, K>>
和K的Javadoc说:
* @param <K> Type of parent, child and sibling tasks
它有抽象的方法,如:
protected abstract K makeChild(Spliterator<P_IN> spliterator);
这种模式实际上显示了一些频率,通常在构建器中,您在基类中有功能(如AbstractTask
),它想要引用类型的具体子类 - 安全的方式。它看起来奇怪的自我指涉,但事实并非如此。
考虑使用类型为Foo
的构建器:
public interface FooBuilder {
FooBuilder a(String s);
FooBuilder b(String s);
Foo build();
}
对于BarBuilder
类型,和Bar
扩展了Foo
:
public interface BarBuilder extends FooBuilder {
BarBuilder c(String s);
Bar build();
}
现在你要创建一个Bar:
Bar b = makeBarBuilder()
.a("a")
.c("c") // ERROR
.build();
您在调用c()
时出错,因为继承的方法a()
返回FooBuilder
,其中没有c()
方法。哎呀。你可以通过使用强制转换来解决这个问题,但也很丑陋,因为你还没有告诉类型系统你可能拥有的关于FooBuilder和BarBuilder之间关系的一切。
这里的治疗方法是使用这种棘手的自我型模式:
BarBuilder
现在您可以创建一个BarBuilder,其继承的Foo方法仍然返回Bar / BarBuilder。当你第一次看到它时它很难读,但非常强大。
答案 1 :(得分:0)
ReduceTask
选择扩展AbstractTask
,使其选择AbstractTask
中的最后一个类型参数作为其自身。这是它的选择。这类似于String
选择实施Comparable<String>
的方式,即它选择Comparable
的类型参数作为其自身。
如果作者想要的话,还可以编写ReduceTask
来选择扩展AbstractTask
,其中最后一个类型参数是其他类型的参数:
class ReduceTask<P_IN, P_OUT, R, S extends AccumulatingSink<P_OUT, R, S>>
extends AbstractTask<P_IN, P_OUT, S, SomeOtherTask<P_IN, P_OUT, R, S>>
(假设SomeOtherTask
满足AbstractTask
中该类型参数的给定界限。)
AbstractTask
在Java中没有办法保证子类扩展它以使最后一个类型参数与类完全相同。