BiConsumer<T, U>
的方法void accept(T t, U u)
提供了两个输入参数的执行,它还有提供一个输入参数的Consumer<T>
的方法void accept(T t)
1.场景再现
背景是这样的,通常对于数据分类或者检索,我们可以基于标签的形式,以便归类,并提供检索功能。
![ER图]](https://img-blog.csdnimg.cn/20200828173940215.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoaWNoZW4yMDEw,size_16,color_FFFFFF,t_70#pic_center)
如上图所示,几张业务表呢,标签存在一个字表中,通过
relation_id
关联主表id,buz_type
标示业务类型(即哪张主表),页面查询时呢,需要展示该数据的相关标签。
如上图,页面展示数据时,需要把每条数据对应的标签一并展示。
既然多个模块的数据查询都需要展示标签,而标签是单独存在一张表中,标签表通过buz_type
和relation_id
可以定位某业务表一条数据的标签,一条数据可以设置多条标签。
/**
* 对相关查询的VO绑定标签
* @param tuple 数据传输通道容器
* @param function 回调函数,返回集合元素对象,让调用方返回对应的主键id
* @param consumer 接口,返回获取的tags
*/
public <T,R> void bindTags(TwoTuple<Integer,List<T>> tuple, Function<T,R> function, BiConsumer<T,List<String>> consumer){
try {
TagForm form = new TagForm();
form.setBuzType(tuple.getA());
List<Tag> tagList = this.pdTagService.queryList(form);
if(CollectionsTools.isNotEmpty(tagList)){
Map<Integer,String> tagMap = tagList.stream().collect(Collectors.toMap(Tag::getSourceId, Tag::getTags));
tuple.getB().forEach(rule -> {
String tags = tagMap.get(function.apply(rule));
if(StringTools.isNotEmpty(tags)){
consumer.accept(rule, Arrays.asList(tags.split(",")));
}
});
}
} catch (Exception e) {
log.error("{}设置标签异常,tuple={}",JSON.toJSON(tuple),e);
}
}
如上述代码,该方法void bindTags(TwoTuple<Integer,List<T>> tuple, Function<T,R> function, BiConsumer<T,List<String>> consumer)
提供了对输入的数据
进行绑定标签的功能特性。下述对参数及进行简单介绍:
- TwoTuple<Integer,List> tuple:支持两个参数的管道容器,
Integer
类型即我们要查询标签的业务类型buz_type
,List<T>
即我们要绑定的数据集。 - Function<T,R> function:即我们关注的数据id,通过ID我们可以得到相应
tagMap
中对应的标签。 - BiConsumer<T,List> consumer:支持两个输入参数的执行,
T
即我们的数据集对象,List<String>
即我们查询得到对应数据集记录的标签集合。
上述封装的这个方法,通过内部包装通过输入的数据集和业务类型,根据业务类型查询出标签,然后给每一个数据集记录对象绑定标签,而方法内部不关注
关联id是哪个参数,通过Function<T,R> function
提供回调机制,让调用方给出,同样对于处理的标签,也通过BiConsumer<T,List<String>> consumer
交由回调函数
决定绑定到数据集对象的某个属性。
我们看一下调用发的代码:
public Result<PageVO<ArgControlVo>> loadRecords(PageForm<ArgControlForm> form) {
if (form.getForm() != null && !StringUtils.isEmpty(form.getForm().getTag())) {
//默认是参数类型
form.getForm().setClassify(15);
}
List<ArgControlVo> voList = new ArgControlConverter().convertList(argControlService.queryByPage(form));
if (CollectionsTools.isEmpty(voList)) {
return Result.failInEmptyRecord(null);
}
int count = voList.size();
if (form.isPaging()) {
count = argControlService.queryCount(form);
}
//查询标签
commonComponent.bindTags(TwoTuple.newInstance(TagConstant.BuzTypeEnum.SY_ARG_CONTROL.getIndex(),voList),
rule->Integer.valueOf(rule.getId()),(rule,tags)->rule.setTags(tags));
return Result.suc(new PageVO<>(count, voList));
}
上述的
commonComponent.bindTags
即我们提前的组件类,通过该组件封装一个公共方法bindTags
,外部通过注入commonComponent
这个bean,并调用该方法。
2.BiConsumer<T, U>
@FunctionalInterface
public interface BiConsumer<T, U> {
/**
* Performs this operation on the given arguments.
*
* @param t the first input argument
* @param u the second input argument
*/
void accept(T t, U u);
/**
* Returns a composed {@code BiConsumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code BiConsumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
方法
void accept(T t, U u);
通过回调给调用方两个参数T、U
,并执行该操作accept
。
3.总结
对于抽象出的方法,我们只关注数据的处理,处理的数据通过回调函数,交由调用方去处理,这时我们可以通过
BiConsumer<T, U>
或者Consumer<T>
。当然,如果我们期望支持三个参数,或者多个,我们也可以进一步封装一个泛型对象,譬如上述的Tuple
,
比如三个参数我们可以定义一个泛型类Tuple<A,B,C>
。