Java内置的四大核心函数式接口

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;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lambda.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值