Java内置的四大核心函数式接口
引子:Java从诞生之日起就一直倡导"一切皆对象",在Java编程里面向对象(OOP)就是编程的一切。但是随着JavaScript、Python、Scala等语言的兴起和新技术的挑战,Java不得不做出相应调整以便支持更加广泛、更加灵活的技术要求,因此Java发展至今不但支持OOP还支持OOF(面向函数编程),很多源码都有OOF的影子,OOF也成为Java必修课之一。
1、消费型接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
// 对andThen的理解 c1.andThen(c2)是将c1和c2合成一个新的c3然再执行accept()
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
// 注意这里返回的是构造Consumer对象的过程
}
}
1、消费型接口无返回值重在消费数据,而不是生产数据,其数据类型由泛型决定。提供两个抽象方法其中accept()
用于对给定的参数执行操作,andThen()
用于链式或组合调用,依次执行操作最后执行after.accept()
,Stream API
中的forEach()、peek()
操作方法就是Consumer
接口类型的。
2、消费型接口使用示例一:我们以缓存增删改为例
@SpringBootTest
public class FunctionInterfaceTest {
private static final Logger log = LoggerFactory.getLogger(FunctionInterfaceTest.class);
@Autowired
private CacheManager cacheManager;
@Test
public void consumerTest() {
String key = "test:key";
Cache cache = cacheManager.getCache("test");
// 1.放入缓存
execute(key, cacheKey -> cache.put(cacheKey, "alex"));
// 2.输出缓存
execute(key, cacheKey -> {
Cache.ValueWrapper wrapper = cache.get(cacheKey);
log.info("hit cache success {} = {} ", key, wrapper.get());
});
// 3.删除缓存
execute(key, cacheKey -> cache.evict(cacheKey));
// 4.验证有没有清空缓存
Cache.ValueWrapper wrapper = cache.get(key);
if (null == wrapper) {
log.info("hit cache error {} = null", key);
}
}
private void execute(String key, Consumer<String> consumer) {
consumer.accept(key);
}
}
注意看上面例子,对于缓存组件的增删改操作,我们只提供一个公共execute()
方法,想执行什么操作完全由我们自己去控制,consumer.accept(key);
前后还可以书写其他业务逻辑代码,这就大大提高了程序的灵活性,完全没有必要删除或者是修改单独提供一个方法。
3、消费型接口使用示例二:将某个字符串先转成大写再转换成小写。
@SpringBootTest
public class FunctionInterfaceTest {
private static final Logger log = LoggerFactory.getLogger(FunctionInterfaceTest.class);
// andThen() 使用方式示例
@Test
public void andThenTest() {
String name = "JavaScript";
execute(name, before -> log.info("before {}", before.toUpperCase()), after -> log.info("after {}", after.toLowerCase()));
}
private void execute(String str, Consumer<String> consumer1, Consumer<String> consumer2) {
consumer1.andThen(consumer2).accept(str);
}
}
再来看输出结果
2、供给型接口
@FunctionalInterface
public interface Supplier<T> {
T get();
}
1、供给型接口Supplier
表示对值的生成操作,仅包含一个无参抽象get()
方法,用于按照由Lambda
表示定义的相关实现,返回一个具体的数据,其数据类型由泛型决定。
2、JDK源码Logger/log()
也有Supplier
的影子
public void log(Level level, Supplier<String> msgSupplier) {
if (!isLoggable(level)) {
return;
}
LogRecord lr = new LogRecord(level, msgSupplier.get());
doLog(lr);
}
3、Supplier
使用示例
@Test
public void supplierTest() {
// 生成 0-9 随机6位数(短信验证码)
StringBuilder code = new StringBuilder();
Supplier<Integer> next = () -> new Random().nextInt(10); // [0-9)
for (int i = 0; i < 6; i++) {
code.append(next.get());
}
log.info("code = {}", code); // code = 286205
}
3、函数型接口
1、Function
是接收一个参数并产生一个结果的函数式接口,常用于从一个只到另外一个只的映射操作,即根据一个类型的数据得到另外一个类型的数据。
源码及抽象方法说明如下
@FunctionalInterface
public interface Function<T, R> {
// 最常用的抽象方法,用于接收一个数据执行相关操作后返回一个新的数据
R apply(T t);
// 组合调用时 先执行后者的映射操作,再执行前者的映射操作 详见案例测试
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
// 和compose相反 组合调用时 先执行前者的映射操作,再执行后者的映射操作
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
// 返回当前正在执行的方法
static <T> Function<T, T> identity() {
return t -> t;
}
}
Stream
API 中与map
相关的操作都是Function
类型的,常用于对象之间的转换即根据方法参数重新映射成新的对象,如下图所示
2、Function
使用示例
@Test
public void functionTest() {
Function<Integer, String> function = number -> "this is a " + number;
log.info("{}", function.apply(123)); // this is a 123
}
@Test
public void test() {
/***** 测试 compose() 与 andThen() 的区别 *****/
Integer num = 100;
// 前者
Function<Integer, Integer> beforeFunction = number -> number * 2;
// 后者
Function<Integer, Integer> afterFunction = number -> number - 2;
// compose() 测试
// 组合调用时 先执行后者的映射操作,再执行前者的映射操作 (100 - 2) * 2 = 196
Integer compose = beforeFunction.compose(afterFunction).apply(num);
log.info("compose = {}", compose); // compose = 196
// andThen() 测试
// 组合调用时 先执行前者的映射操作,再执行后者的映射操作 (100 * 2) - 2 = 198
Integer andThen = beforeFunction.andThen(afterFunction).apply(num);
log.info("andThen = {}", andThen); // andThen = 198
}
4、断定型接口
1、Predicate
断定也称为断言,是一个接收单个参数返回一个布尔值的函数式接口,表示了对输入参数的判断。
2、Stream
API 中 filter()
方法就是Predicate
断定型的接口,如
@FunctionalInterface
public interface Predicate<T> {
// 最常用的抽象方法 用来处理参数T是否满足要求 可以理解为 条件A
boolean test(T t);
// 用于组合调用 可以理解为 条件A && 条件B
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
// test() 结果取反 可以理解为 !条件A
default Predicate<T> negate() {
return (t) -> !test(t);
}
// 用于组合调用 进行"或"操作 可以理解为 条件A || 条件B
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
// 对当前操作进行 "=" 操作 可以理解为 A == B
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
3、Predicate
断定型函数式接口使用示例
@SpringBootTest
public class FunctionInterfaceTest {
private static final Logger log = LoggerFactory.getLogger(FunctionInterfaceTest.class);
@Test
public void test() {
List<String> list = Arrays.asList("ab", "1", "x", "456");
// 手动实现 stream API 中 filter 功能
// 过滤能转换成数字的元素
List<String> filter = filter(list, item -> Pattern.compile("[0-9]*").matcher(item).matches());
log.info("{}", filter.toString()); // [1, 456]
}
public static <E> List<E> filter(List<E> list, Predicate<E> predicate) {
List<E> targetList = new ArrayList<>();
for (E e : list) {
if (predicate.test(e)) {
targetList.add(e);
}
}
return targetList;
}
}