盘点java8 stream中隐藏的函数式接口

2024-09-01 22:31:59 浏览数 (1)

shigen坚持更新文章的博客写手,记录成长,分享认知,留住感动。个人IP:shigen

提到函数式接口,最常见的就是lambda表达式,IDEA也有智能的提示:

idea智能提示idea智能提示

最后改成这样的就是最简洁的、IDEA希望的风格:

代码语言:java复制
List<String> nameList = users.stream().map(User::getName).collect(Collectors.toList());
log.info("nameList:{}", nameList);

直接一行搞定比较复杂的功能。今天要讲到的函数式接口就从这个展开。

1. 函数式接口

所谓的函数式接口指的是只定义一个抽象方法的接口。接口类常用这个注解@FunctionalInterface表明:

  • java.util.Comparator
代码语言:java复制
public interface Comparator<T> {
    int compare(T o1, T o2);
}
  • java.lang.Runnable
代码语言:java复制
public interface Runnable {
    void run();
}
  • java.util.concurrent.Callable
代码语言:java复制
public interface Callable<V> {
    V call() throws Exception;
}

实现起来基本上是这样的:

代码语言:java复制
(o1, o2) -> o1.getXxx() > o2.getXxx()
() -> void
() -> V类型的对象

其中,callable最常见在多线程上,这里展示下我对于分布式锁的封装:

callable案例:分布式锁封装callable案例:分布式锁封装

2. 常用的函数式接口

或许对于Function、Predicate等等你并不陌生,好熟悉、好像在哪里用过就是想不起来!我们还是用stream()流来讲解。前提是构建一个用户列表:

构建数据构建数据

相信截图里User类的属性一目了然。现在我们用stream流来操作。

2.1 Function出现
代码语言:java复制
List<String> nameList = users.stream().map(user -> user.getName()).collect(Collectors.toList());
log.info("nameList:{}", nameList);

这里的逻辑很简单,获得所有的用户名字返回集合。不知道有没有好奇过map,他的参数是什么:

map的参数map的参数

额,就是一个Function!好的,我这样改造下:

代码语言:java复制
    static Function<User, String> nameFunction = user -> user.getName();

    public static void main(String[] args) {

        List<String> nameList = users.stream().map(nameFunction).collect(Collectors.toList());
        log.info("nameList:{}", nameList);
    }

其实Function就是两个类型约束,一个数参数类型,一个是返回值类型,定义了一个固定的逻辑。这个还好,如果稍加对于name的处理,并且是通用的处理方式,就可以考虑用Function写成一个通用的方法。

2.2 Predicate出现

Function类似,Predicate顾名思义,就是断言。可以从stream.filter()中获得:

代码语言:java复制
User user1 = users.stream().filter(user -> "李四".equals(user.getName())).findFirst().orElse(null);
log.info("user1:{}", user1);
filter-predicatefilter-predicate

对应的改造:

代码语言:java复制
    static Predicate<User> namePredicate = user -> "李四".equals(user.getName());

    public static void main(String[] args) {
        User user1 = users.stream().filter(namePredicate).findFirst().orElse(null);
        log.info("user1:{}", user1);
    }

Predicate的范型只有一个,就是一个对象,返回的就是断言的方法。

2.3 Cousumer的出现

直接给出代码案例:

代码语言:java复制
        Consumer<User> userConsumer = user -> {
            String name = user.getName();
            log.info("name:{}", name);
        };

        userConsumer.accept(users.get(0));

适用场景:访问类型T的对象,对其执行某些操作。只有操作,没有返回值,也不需要关注返回值。另外附上我最近看到的一段模板代码:

代码语言:java复制
    /**
     * 利用设计模式减少样板代码
     */
    public static void readLine(String filename, Consumer<String> consumer) {
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = reader.readLine()) != null) {
                consumer.accept(line);
            }
        } catch (IOException e) {
            throw new RuntimeException("Error reading file", e);
        }
    }



    public static void main(String[] args) {
        String filePath = "/Users/shigen/Downloads/area.json";
        readLine(filePath, (lines) -> {
            System.out.println(lines);
        });
    }  
2.4 Supplier的出现

还是先上代码:

代码语言:java复制
        Supplier<User> userSupplier = () -> new User("10005", "shigen", null);
        User user2 = userSupplier.get();
        log.info("user2:{}", user2);
        userConsumer.accept(user2);

这里正好和Consumer相反,这里是生产对象的,然后可以供它消费。

适用场景:定义类型T的对象的生产规则。这里列出一个案例:生成随机数的方法:

代码语言:java复制
        Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
        log.info("randomSupplier:{}", randomSupplier.get());

突然感觉曾经写过的工具类可以用这种方式简化一下了。

2.5 Comparator的出现

其实我们用的最多的还是实现Comparator的接口,重写compare方法用来比较对象,多见于需要内存排序的场景:

Comparator接口Comparator接口

也可以在stream.sorted()的参数中看出来:

stream.sorted()stream.sorted()
代码语言:java复制
        Comparator<User> userComparator =
            (user4, user5) -> user5.getName().compareTo(user4.getName());

        List<String> sortedName = users.stream().sorted(userComparator).map(User::getId).collect(Collectors.toList());
        log.info("sortedName:{}", sortedName);

当然,其它的高级复合Lambda表达式用法,可以参考这篇文章:系统学习lambda表达式。个人认为在业务代码使用复合lambda表达式,会加重代码的理解难度,不推荐。了解常见的函数式接口,并会使用即可。

与shigen一起,每天不一样!

0 人点赞