深入了解Java 8 新特性:lambda表达式进阶

前言

Java 8中的Lambda表达式是一种匿名函数,它允许你将函数作为方法参数进行传递,或者把代码更简洁地定义在你的应用程序里。另外Java的函数式编程就是Lambda表达式,java的函数式接口的有一个明显特征:有且仅有一个抽象方法的接口。下面是一些常见的Java内置的函数式接口梳理,掌握这些内置的函数式接口是相当有必要的,因为作为一种更简洁、更灵活和更易于维护的编程方式,这在很多的java相关的框架技术中有大量的应用,相信有喜欢钻研源码小伙伴应该深有体会。

Java内置的函数式接口

Function

Function接口是Java 8中引入的一个函数式接口,它位于java.util.function包中。这个接口表示一个输入参数能够产生一个结果的函数。Function接口只有一个抽象方法,即apply(T t),它接受一个参数并返回一个结果。

typescript 复制代码
@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));
    }

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

下面是一个使用Function接口的简单示例:

typescript 复制代码
@Test
public void test5() {
    Function<String, Student> function = str -> new Student(str);
    Student student = function.apply("张三");
    log.info(student.getName());
}

在Java中,Function接口的默认实现包括以下几种:

  • Function. andThen(Function after):这个方法返回一个新函数,它首先应用原始函数,然后应用另一个函数。也就是说,这个新函数首先将输入映射到某个结果,然后将结果映射到另一个结果。假设我们有一个Function,它接受一个整数作为输入,并返回一个整数作为输出。现在,我们想在这个函数的基础上添加一个新的函数,将得到的整数加倍。
ini 复制代码
@Test
public void test7() {
    Function<Integer, Integer> originalFunction = x -> x * 2;
    Function<Integer, Integer> newFunction = x -> x * 2;
    Function<Integer, Integer> composedFunction = originalFunction.andThen(newFunction);
    int input = 5;
    int result = composedFunction.apply(input);
    System.out.println(result);  // 输出:20
}

在上面的示例中,我们定义了一个原始函数originalFunction,它接受一个整数作为输入,并将其乘以2。然后,我们定义了一个新的函数newFunction,它也接受一个整数作为输入,并将其乘以2。接下来,我们使用andThen方法将这两个函数组合成一个新的函数composedFunction。最后,我们将输入整数5传递给composedFunction,并打印结果。在这个例子中,首先将输入整数5乘以2得到10,然后将10乘以2得到20,最终输出结果为20。

  • Function. compose(Function before):这个方法返回一个新函数,它首先应用另一个函数,然后将结果映射到原始函数的输入。也就是说,这个新函数首先将输入映射到另一个输入,然后将这个输入映射到结果。假设我们有两个函数,一个将字符串转换为大写,另一个将字符串长度截断为5个字符。这两个函数可以组合成一个新函数,首先将字符串转换为大写,然后将其长度截断为5个字符。
ini 复制代码
@Test
public void test6(){
    Function<String, String> toUpperCase = str -> str.toUpperCase();
    Function<String, String> truncate = str -> str.substring(0, 6);
    Function<String, String> composedFunction = toUpperCase.compose(truncate);
    String input = "hello world!";
    String result = composedFunction.apply(input);
    System.out.println(result);  // 输出:"HEL"
}

在上面的示例中,我们首先定义了两个函数:toUpperCase和truncate。然后,我们使用compose方法将它们组合成一个新函数composedFunction。最后,我们调用composedFunction对输入字符串进行操作,并打印结果。

BiFunction

BiFunction接口是Java 8中引入的一个函数式接口,它位于java.util.function包中。BiFunction接口表示一个接受两个输入参数并返回一个结果的函数。它与Function接口类似,但多了一个输入参数。

BiFunction接口的定义如下:

scss 复制代码
@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

其中,T和U是输入参数的类型,R是返回结果的类型。apply()方法是抽象的,需要具体的实现来提供具体的逻辑。

下面是一个使用BiFunction接口的示例:

csharp 复制代码
@Test
public void test8() {
    BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;
    int result = add.apply(2, 3);
    System.out.println(result);  // 输出:5
}

在上面的示例中,我们定义了一个add函数,它接受两个整数作为输入,并将它们相加得到一个整数作为结果。我们使用apply()方法调用该函数并传递两个参数2和3,然后打印结果5。

BiFunction接口包含一个默认实现,即andThen方法,用于将两个函数组合在一起。它接受一个BiFunction作为参数,并返回一个新的函数,该函数将先应用原始函数,然后应用给定的函数。

ini 复制代码
@Test
public void test9() {
    BiFunction<Integer, Integer, String> addAndToString = (x, y) -> Integer.toString(x + y);
    Function<String, String> toUpperCase = str -> str.toUpperCase();
    BiFunction<Integer, Integer, String> composedFunction = addAndToString.andThen(toUpperCase);
    int input1 = 2;
    int input2 = 3;
    String result = composedFunction.apply(input1, input2);
    System.out.println(result);  // 输出:"5"  
}

在上面的示例中,我们定义了一个addAndToString函数,它接受两个整数作为输入,并将它们的和转换为字符串。然后,我们定义了一个toUpperCase函数,它接受一个字符串作为输入,并将其转换为大写。接下来,我们使用andThen方法将两个函数组合成一个新的函数composedFunction。最后,我们调用composedFunction对输入整数2和3进行操作,并打印结果"5"。首先将输入整数2和3相加得到5,然后将5转换为字符串"5",最后将字符串"5"转换为大写"5",最终输出结果为"5"。

Predicate

Predicate 是 Java 中的一个函数式接口,它位于 java.util.function 包中。这个接口用于表示一个参数的谓词(即,条件判断),接收一个参数并返回一个布尔值,通常用于判断参数是否满足指定的条件

scss 复制代码
@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }

    @SuppressWarnings("unchecked")
    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}

Predicate接口的主要方法是 test(),它接收一个参数并返回一个布尔值。下面是一个简单的例子:

ini 复制代码
@Test
public void test3(){
    Student student = new Student("张三");
    Predicate<Student> predicate= obj -> "张三".equals(obj.getName());
    boolean flag = predicate.test(student);
    Assert.isTrue(flag,"test fail");
    boolean flag2 = predicate.test(new Student("李四"));
    Assert.isTrue(flag2,"test fail");
}

Predicate:常用的四个方法:

  • boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值。
  • default Predicate negate():返回一个逻辑的否定,对应逻辑非。
  • default Predicate and(Predicate other):返回一个组合判断,对应短路与。
  • default Predicate or(Predicate other):返回一个组合判断,对应短路或。
ini 复制代码
@Test
public void test4(){
    Student student = new Student("张三");
    Predicate<Student> predicate= obj -> "张三".equals(obj.getName());
    Predicate<Student> predicate2= obj-> "李四".equals(obj.getName());
    Predicate<Student> or = predicate2.or(predicate);
    boolean flag = or.test(student);
    Assert.isTrue(flag,"test fail");
    student.setName("李四");
    boolean flag2 = or.test(student);
    Assert.isTrue(flag2,"test fail");
    Predicate<Student> predicate3=obj->18==obj.getAge();
    student.setAge(18);
    Predicate<Student> and = predicate3.and(predicate2);
    boolean flag3 = and.test(student);
    Assert.isTrue(flag3,"test fail");
    student.setName("铁蛋");
    boolean test = and.test(student);
    Assert.isTrue(test,"学生姓名不等于张三或者李四");
}

Consumer

Consumer接口代表了一个接受输入参数并返回void的函数。它的主要作用是消费输入数据,也就是说,对输入进行某种操作,但不返回任何结果。

scss 复制代码
@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

Consumer接口的主要方法是void accept(T t):对给定的参数执行此操作。

typescript 复制代码
@Test
public void test2() {
    Student student = new Student("zhangsan");
    Consumer<String> nameConsumer = str -> student.setName(str);
    nameConsumer.accept("lisi");//设置学生的姓名为拼音-小写
    log.info(student.getName());
    Assert.isTrue("lisi".equals(student.getName()), "test fail!");
}

Consumer接口的默认实现,andThen方法,用于将两个Consumer合并在一起。它允许你将一个操作(即第二个Consumer)附加到另一个操作(即第一个Consumer)的后面,以便在原始操作完成之后执行附加操作。

下面是一个简单的示例,演示如何使用andThen方法:

ini 复制代码
@Test
public void test2() {
    Student student = new Student("zhangsan");
    Consumer<String> nameConsumer = str -> student.setName(str);
    Consumer<String> nameConsumer2 = name -> student.setName(name.toUpperCase());
    Consumer<String> nameConsumer3 = nameConsumer.andThen(nameConsumer2);//设置学生的姓名为拼音-大写
    nameConsumer3.accept("lisi");
    log.info(student.getName());
    Assert.isTrue("LISI".equals(student.getName()), "test fail!");
}

Supplier

这个接口表示一个不接受任何参数但返回结果的函数

csharp 复制代码
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

特点:

  • 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据。
  • Supplier接口也称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。

使用示例:

typescript 复制代码
private String getStuName(Supplier<Student> supplier) {
    String name = supplier.get().getName();
    return name;
}

@Test
public void test() {
    String name = this.getStuName(() -> new Student("zhangsan"));
    log.info(name);
    Assert.isTrue(name.equals("zhangsan"), "test fail");
}
相关推荐
hello早上好5 分钟前
1-分布式架构设计理论
后端·架构
孟紫瑶17 分钟前
Swift语言的移动应用测试
开发语言·后端·golang
AI小匠30 分钟前
私有化部署MinerU 与应用实践
后端
星星不打輰30 分钟前
MybatisPlus
java·mybatis
hxung32 分钟前
如何保证mysql和redis的数据一致性
java·数据库·redis·mysql
激动滴西瓜32 分钟前
使用 Spring AI 构建高效的AI Agent(Part 1)
人工智能·后端
jieyucx35 分钟前
深入剖析C语言中的指针与数组
java·c语言·算法
我要学编程(ಥ_ಥ)37 分钟前
初始JavaEE篇 —— SpringBoot 统一功能处理
java·spring boot·后端·spring·java-ee
Huazie40 分钟前
【Spring Boot 源码学习】深入 ConfigurableEnvironment 的初始化过程
java·spring boot·源码阅读
bobz9651 小时前
AI02 相关领域
后端