我正在构建一个大致像这样的流利的API(假设存在一个带有吸气剂Person
的类getId
并返回一个Long
):
String result = context.map(Person::getId)
.pipe(Object::toString)
.pipe(String::toUpperCase)
.end(Function.identity())
如您所见,只有.end
函数充当终端操作员。即使前一个.end(Function.identity())
调用已经具有正确的类型,我也常常不得不以.pipe
调用结尾,这使该API的整体用法混乱。
是否有什么方法可以使Fluent-API成为终端操作员和“桥接操作员”?我只是不想用像pipe
这样的专用pipeTo
-变量(仅接受Function<CurrentType, ExpectedType>
并内部调用.end
的专用class Context<InType, CurrentType, TargetType> {
private final Function<InType, CurrentType> getter;
public Context(Function<InType, CurrentType> getter) {
this.getter = getter;
}
public <IntermediateType> Context<InType, IntermediateType, TargetType>
pipe(Function<CurrentType, IntermediateType> mapper) {
return new Context<>(getter.andThen(mapper));
}
public Function<InType, TargetType> end(Function<CurrentType, TargetType> mapper) {
return getter.andThen(mapper);
}
}
//usage
Function<Person, String> mapper = new Context<Person, Long, String>(Person::getId)
.pipe(Object::toString)
.pipe(String::toUpperCase)
.end(Function.identity());
mapper.apply(new Person(...))
-变量使API混乱)迫使用户考虑对我来说似乎不必要的API的非常特定的部分。
编辑: 根据要求简化的上下文实现:
EventListener
答案 0 :(得分:1)
如果我了解您的需求,我会重载end()
并摆脱最后的函数组成:
public Function<InType, CurrentType> end() {
return this.getter;
}
再想一想,我认为Context
类的第三个类型参数可以消除,因为仅在方法级别需要中间类型。检查一下:
class OtherContext<I, O> {
private final Function<I, O> getter;
public OtherContext(Function<I, O> getter) {
this.getter = getter;
}
public <T> OtherContext<I, T> pipe(Function<O, T> mapper) {
return new OtherContext<I, T>(getter.andThen(mapper));
}
public <T> Function<I, T> end(Function<O, T> mapper) {
return getter.andThen(mapper);
}
public Function<I, O> end() {
return getter;
}
}
答案 1 :(得分:1)
您不能在Java中定义具有相同名称和不同返回类型的方法。您的方法可能返回类似Wrapped<T>
的内容,而您想返回T
。通常,我可能建议为每种方法都使用类似*andEnd(...)
的方法。因此pipeAndEnd(...)
将完成管道操作,然后以终端操作结束。这样做可能会很乏味,因此如果您有很多方法,您可能希望研究一些代码生成。
另一方面,似乎您正在实现自己版本的Stream API。除非您这样做是出于教育目的,否则使用现有的,经过良好测试/记录的代码(尤其是标准jdk的代码部分)总是比重新实现自己的同一版本更好。
答案 2 :(得分:0)
我遇到的主要问题是任何pipe
步骤都可能是终端操作。正如下面的讨论中概述的那样,每个答案和主要帖子:在Java中不可能两次使用具有相同名称的函数,并且将其用作终端操作。
我为解决这个问题而head之以鼻,并尝试了多种方法,每种方法均无效。那就是当我意识到我在做什么与Java Stream
-API基本上相同时:您有一个源(源),做一些花哨的东西(管道),然后结束(收集)。如果我们对问题采用相同的方案,则无需pipe
进行终端操作,我们只需要另一个操作(例如end
)作为终点即可。由于我对何时可能结束有一些扩展的要求(当前类型必须与另一种类型匹配),因此我通过只允许特定于上下文的功能实现了end
,而对于该功能而言,只有一种合理的实现可用(很难解释)。这是当前实现的示例(pipe
已重命名为map
,end
重命名为to
):
Mapper<Person, PersonDTO> mapper = Datus.forTypes(Person.class, PersonDTO.class).immutable(PersonDTO::new)
.from(Person::getFirstName).to(ConstructorParameter::bind)
.from(Person::getLastName)
.given(Objects::nonNull, ln -> ln.toUpperCase()).orElse("fallback")
.to(ConstructorParameter::bind)
.build();
如您所见,.to
充当终端运算符,如果当前类型与预期类型不匹配,ConstructorParameter::bind
将抱怨类型不匹配。