什么是函数式接口
有且只有一个抽象方法的接口被称为函数式接口,函数式接口适用于函数式编程的场景,Lambda就是Java中函数式编程的体现,可以使用Lambda表达式创建一个函数式接口的对象,一定要确保接口中有且只有一个抽象方法,这样Lambda才能顺利的进行推导。
与@Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法(equal和hashcode方法不算),否则将会报错。但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。
Consumer<T>: 消费型接口
Consumer通过名字可以看出它是一个消费函数式接口,主要针对的是消费(1…n 入参, 无返回)这个场景,它的代码定义如下:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
通过泛型 T 定义了一个入参,但是没有返回值,它代表你可以针对这个入参做一些自定义逻辑,比较典型的例子是forEach方法。
例子:
List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
list.foreach(System.out::println); //打印数组
Supplier<T>: 供给型接口
Supplier通过名字比较难看出来它是一个场景的函数式接口,它主要针对的是说获取(无入参,有返回)这个场景,它的代码定义如下:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
通过泛型 T 定义了一个返回值类型,但是没有入参,它代表你可以针对调用方获取某个值,比较典型的例子是 Stream中的collect方法,通过自定义传入我们想要取得的某种对象进行对象收集。
例子:
List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
List<String> newList = list.stream().filter(x -> x >= 2).collect(Collectors.toList());
// 将大于等于2的数重新收集成一个集合,其中Collectors.toList()的函数原型为
// new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,(left, right) -> { left.addAll(right); return left; },CH_ID)
// 原型中的ArrayList::new即为Supplier类型
Function<T,R>: 函数型接口
Function接口的名字不太能轻易看出来它的场景,它主要针对的则是 转换(有入参,有返回,其中T是入参,R是返回)这个场景,其实说转换可能也不太正确,它是一个覆盖范围比较广的场景,你也可以理解为扩展版的Consumer,接口定义如下:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
通过一个入参T进行自定义逻辑处理,最终得到一个出参R,比较典型的例子是Stream中的map系列方法和reduce系列方法。
例子:
List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
List<Integet> newList = list.stream().map(Integer::parseInt).collect(Collectors.toList());
// map将list中所有的元素的类型由 String 通过 Integer.parseInt的方式转换为Intger。 简单来说就是A => B;
/**
* 权益转换
*/
private final Function<BenefitDTO,BenefitResponse> BENEFIT_RESPONSE_CONVERTOR = benefitDTO -> {
BenefitResponse benefitResponse = new BenefitResponse();
benefitResponse.setId(benefitDTO.getBenefitId());
benefitResponse.setBenefitName(benefitDTO.getBenefitName());
benefitResponse.setStartTime(benefitDTO.getStartTime());
benefitResponse.setEndTime(benefitDTO.getEndTime());
benefitResponse.setChannel(benefitDTO.getBenefitChannel());
benefitResponse.setSellerId(benefitDTO.getSellerId());
return benefitResponse;
};
List<BenefitResponse> benefitResponseList = benefitList.stream().map(BENEFIT_RESPONSE_CONVERTOR).collect(Collectors.toList());
Predicate<T>: 断言型接口
Predicate主要针对的是判断(有入参,有返回,凡是返回的类型固定为Boolean。可以说Function是包含Predicate的 )这个场景,它的代码定义如下:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
通过泛型 T 定义了一个入参,返回了一个布尔值,它代表你可以传入一段判断逻辑的函数,比较典型的例子是Stream中的filter方法。
List<String> list = Lists.newArrayList("1", "2", "3", "4", "5", "6");
List<String> newList = list.stream().filter(x -> x >= 2).collect(Collectors.toList());
// 将大于等于2的数重新收集成一个集合,filter中的 x -> x >= 2就是Predicate接口
Bi类型接口
BiConsumer、BiFunction、BiPrediate是Consumer、Function、Predicate 的扩展,可以传入多个参数,没有BiSupplier是因为Supplier没有入参。
BiConsumer接口接收两个泛型参数,对这两个参数做消费处理;使用这个函数式接口的终端操作常用的是遍历map。
@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);
}
Map接口的终端操作,forEach的参数就是BiConsumer函数接口,对HashMap的数据进行消费,示例如下。
Map<String, String> map = new HashMap<>();
map.put("a", "a");
map.put("b", "b");
map.put("c", "c");
map.put("d", "d");
map.forEach((k, v) -> {
System.out.println(k+“,”+v);
});
操作基本数据类型的接口
IntConsumer、IntFunction、IntPredicate、IntSupplier、LongConsumer、LongFunction、LongPredicate、LongSupplier、DoubleConsumer、DoubleFunction、DoublePredicate、DoubleSupplier。
其实常用的函数式接口就那四大接口Consumer、Function、Prediate、Supplier,其他的函数式接口就不一一列举了,可以去java.util.function包下看源码。