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

简介: `shigen`是一位坚持更新文章的博客作者,记录成长历程,分享认知见解,留住感动瞬间。本文介绍了函数式接口的概念及其在Java中的应用,包括`Comparator`、`Runnable`、`Callable`等常见接口,并详细讲解了`Function`、`Predicate`、`Consumer`、`Supplier`和`Comparator`等函数式接口的使用方法及应用场景,展示了如何利用这些接口简化代码并提高编程效率。**个人IP:shigen**,与shigen一起,每天进步一点点!

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

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

idea智能提示

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

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

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

1. 函数式接口

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

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

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

(o1, o2) -> o1.getXxx() > o2.getXxx()
() -> void
() -> V类型的对象

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

callable案例:分布式锁封装

2. 常用的函数式接口

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

构建数据

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

2.1 Function出现

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

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

map的参数

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

    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()中获得:

User user1 = users.stream().filter(user -> "李四".equals(user.getName())).findFirst().orElse(null);
log.info("user1:{}", user1);

filter-predicate

对应的改造:

    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的出现

直接给出代码案例:

        Consumer<User> userConsumer = user -> {
   
   
            String name = user.getName();
            log.info("name:{}", name);
        };

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

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

    /**
     * 利用设计模式减少样板代码
     */
    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的出现

还是先上代码:

        Supplier<User> userSupplier = () -> new User("10005", "shigen", null);
        User user2 = userSupplier.get();
        log.info("user2:{}", user2);
        userConsumer.accept(user2);

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

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

        Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
        log.info("randomSupplier:{}", randomSupplier.get());

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

2.5 Comparator的出现

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

Comparator接口

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

stream.sorted()

        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一起,每天不一样!

目录
相关文章
|
14天前
|
存储 Java API
Java——Stream流详解
Stream流是JDK 8引入的概念,用于高效处理集合或数组数据。其API支持声明式编程,操作分为中间操作和终端操作。中间操作包括过滤、映射、排序等,可链式调用;终端操作则完成数据处理,如遍历、收集等。Stream流简化了集合与数组的操作,提升了代码的简洁性
36 11
Java——Stream流详解
|
19天前
|
Java
Java——抽象类和接口
抽象类是一种不能被实例化的类,至少包含一个抽象方法(无实现体的方法),常用于定义一组相关类的共同特征,并强制子类实现特定方法。抽象方法不能被 `static` 或 `final` 修饰,且必须被重写。 接口则是一个完全抽象的类,用于规范类的行为。接口使用 `interface` 关键字定义,不能实例化,并且类与接口之间是实现关系。 内部类是在一个类内定义的类,分为成员内部类、静态内部类、局部内部类和匿名内部类。成员内部类可被修饰符修饰,静态内部类只能访问外部类的静态成员,局部内部类定义在方法内,匿名内部类则隐藏了名字,直接通过 `new` 关键字定义并实现接口或继承类。
14 5
Java——抽象类和接口
|
19天前
|
Java
Java——接口的使用实例
Comparable接口用于自定义类的对象比较。通过实现此接口并重写`compareTo`方法,可以定义自定义类型的比较规则。 接下来介绍了Comparator接口,它提供了一种更灵活的比较方式。通过实现Comparator接口并重写`compare`方法,可以根据不同属性定义不同的比较规则。例如,定义一个`BrandComparator`类来比较汽车的品牌。 最后,介绍了Cloneable接口,用于实现对象的克隆。实现该接口并重写`clone`方法后,可以创建对象的浅拷贝或深拷贝。浅拷贝仅复制对象本身,深拷贝则会递归复制所有成员变量。
15 4
Java——接口的使用实例
|
5天前
|
Java 测试技术
Java接口的生产环境应用注意点
在Java生产环境中,合理使用接口对提升代码质量至关重要。设计接口时应遵循单一职责原则,采用清晰命名,并控制方法数量。默认方法应谨慎使用,避免与实现类产生冲突。通过版本化管理接口更新,确保向后兼容。实现接口时需明确行为,保持实现与接口分离,利用多态增强灵活性。关注性能影响,适当文档注释及充分测试确保接口稳定可靠。综合运用这些策略,可以显著提高系统的可扩展性和维护性。
|
5天前
|
Java
Java 接口的简化理解
Java 接口是一种强大的概念,用于定义方法签名而非具体实现,作为行为规范,强调功能而非实现细节。接口是特殊的引用类型,包含常量和方法签名。其特点包括:无实现方法体、支持多重继承、内置常量定义。通过示例展示了如何定义和实现接口,以及如何通过接口引用调用实现类的方法。接口的应用场景包括抽象化、插件架构和松耦合设计。从 Java 8 起,接口还支持默认方法和静态方法,进一步增强了其灵活性和扩展性。理解接口是 Java 编程的基础之一。
|
14天前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
|
6天前
|
Java
接口和抽象类【Java面向对象知识回顾②】
本文讨论了Java中抽象类和接口的概念与区别。抽象类是不能被实例化的类,可以包含抽象和非抽象方法,常用作其他类的基类。接口是一种纯抽象类型,只包含抽象方法和常量,不能被实例化,且实现接口的类必须实现接口中定义的所有方法。文章还比较了抽象类和接口在实现方式、方法类型、成员变量、构造方法和访问修饰符等方面的不同,并探讨了它们的使用场景。
接口和抽象类【Java面向对象知识回顾②】
|
9天前
|
Java 程序员 API
Java 8新特性之Lambda表达式与Stream API的探索
【9月更文挑战第24天】本文将深入浅出地介绍Java 8中的重要新特性——Lambda表达式和Stream API,通过实例解析其语法、用法及背后的设计哲学。我们将一探究竟,看看这些新特性如何让Java代码变得更加简洁、易读且富有表现力,同时提升程序的性能和开发效率。
|
8天前
|
SQL Java Linux
Java 8 API添加了一个新的抽象称为流Stream
Java 8 API添加了一个新的抽象称为流Stream
|
13天前
|
Java
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
22 7
下一篇
无影云桌面