它的嵌套相当深,但似乎并不是特别困难。
第一个观察结果是,如果 for 循环转换为流,则可以使用 将嵌套的 for 循环“平展”为单个流。此操作采用单个元素,并在流中返回任意数量的元素。我查了一下,发现返回一个数组,所以我们用把它变成一个流。(我对 和 做了类似的假设。flatMap
StandardServer.findServices()
Service
Arrays.stream()
Engine.findChildren()
Host.findChildren()
接下来,每个循环中的逻辑执行检查和强制转换。这可以使用流作为执行操作的操作进行建模,然后执行仅强制转换并返回相同引用的操作。这实际上是一个no-op,但它允许静态类型系统将a转换为a。instanceof
filter
instanceof
map
Stream<Container>
Stream<Host>
将这些转换应用于嵌套循环,我们得到以下结果:
public List<ContextInfo> list() {
final List<ContextInfo> list = new ArrayList<ContextInfo>();
final StandardServer server = getServer();
Arrays.stream(server.findServices())
.filter(service -> service.getContainer() instanceof Engine)
.map(service -> (Engine)service.getContainer())
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.filter(possibleHost -> possibleHost instanceof Host)
.map(possibleHost -> (Host)possibleHost)
.flatMap(host -> Arrays.stream(host.findChildren()))
.filter(possibleContext -> possibleContext instanceof Context)
.map(possibleContext -> (Context)possibleContext)
.forEach(context -> {
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
list.add(info);
});
return list;
}
但是等等,还有更多。
最终操作是一个稍微复杂一些的操作,它将 a 转换为 .此外,这些只是收集到一个中,因此我们可以使用收集器来执行此操作,而不是在前面创建和空列表,然后填充它。应用这些重构会产生以下结果:forEach
map
Context
ContextInfo
List
public List<ContextInfo> list() {
final StandardServer server = getServer();
return Arrays.stream(server.findServices())
.filter(service -> service.getContainer() instanceof Engine)
.map(service -> (Engine)service.getContainer())
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.filter(possibleHost -> possibleHost instanceof Host)
.map(possibleHost -> (Host)possibleHost)
.flatMap(host -> Arrays.stream(host.findChildren()))
.filter(possibleContext -> possibleContext instanceof Context)
.map(possibleContext -> (Context)possibleContext)
.map(context -> {
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
return info;
})
.collect(Collectors.toList());
}
我通常尝试避免多行 lambda(例如在最终操作中),所以我会将其重构为一个小的帮助器方法,该方法采用 a 并返回 .这根本不会缩短代码,但我认为它确实使它更清晰。map
Context
ContextInfo
更新
但是等等,还有更多。
让我们将调用提取到它自己的管道元素中:service.getContainer()
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.filter(container -> container instanceof Engine)
.map(container -> (Engine)container)
.flatMap(engine -> Arrays.stream(engine.findChildren()))
// ...
这暴露了重复过滤,然后是带有强制转换的映射。这总共做了三次。似乎其他代码可能需要执行类似操作,因此将这部分逻辑提取到帮助器方法中会很好。问题是可以更改流中的元素数量(删除不匹配的元素),但无法更改其类型。并且可以更改元素的类型,但不能更改其数量。某些东西可以同时改变数量和类型吗?是的,又是我们的老朋友了!因此,我们的帮助器方法需要获取一个元素并返回不同类型的元素流。该返回流将包含单个强制转换的元素(如果匹配)或为空(如果它不匹配)。帮助程序函数将如下所示:instanceof
filter
map
flatMap
<T,U> Stream<U> toType(T t, Class<U> clazz) {
if (clazz.isInstance(t)) {
return Stream.of(clazz.cast(t));
} else {
return Stream.empty();
}
}
(这大致基于一些注释中提到的C#的构造。OfType
当我们在这里时,让我们提取一个方法来创建一个:ContextInfo
ContextInfo makeContextInfo(Context context) {
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
return info;
}
完成这些提取后,管道如下所示:
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.flatMap(container -> toType(container, Engine.class))
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.flatMap(possibleHost -> toType(possibleHost, Host.class))
.flatMap(host -> Arrays.stream(host.findChildren()))
.flatMap(possibleContext -> toType(possibleContext, Context.class))
.map(this::makeContextInfo)
.collect(Collectors.toList());
我认为更好,我们已经删除了可怕的多行语句lambda。
更新:奖金挑战
再一次,是你的朋友。取流尾,并将其迁移到尾部之前的最后一个。这样,变量仍在作用域内,您可以将其传递给已修改为也采用的帮助器方法。flatMap
flatMap
host
makeContextInfo
host
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.flatMap(container -> toType(container, Engine.class))
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.flatMap(possibleHost -> toType(possibleHost, Host.class))
.flatMap(host -> Arrays.stream(host.findChildren())
.flatMap(possibleContext -> toType(possibleContext, Context.class))
.map(ctx -> makeContextInfo(ctx, host)))
.collect(Collectors.toList());