Java 8 “新”特性详解:Lambda、函数式接口、Stream、Optional 与方法引用

1、Lambda表达式

1.1、为什么使用Lambda表达式

Lambda是一个匿名函数 ,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

Lambda表达式的本质 :作为函数式接口的实例

1.2、Lambda表达式的使用

1.2.1、举例

像重写Comparator中的compare方法:

java 复制代码
(o1,o2) -> Integer.compare(o1,o2);

1.2.2、格式

->:lambda操作符 或 箭头操作符

->左边:Lambda形参列表 (其实就是接口中的抽象方法的形参列表)

->右边:Lambda体(其实就是重写的抽象方法的方法体)

1.3、Lambda表达式的六种使用情况

->左边:Lambda形参列表的参数类型可以省略(类型推断);如果Lambda形参列表只有一个参数,则那一对( )也可以省略。

->右边:Lambda体应该使用一对{}包裹:如果Lambda体只有一条执行语句(可能是return语句),可以省略这一对{}和return关键字。

1.3.1、语法格式一:无参,无返回值

示例代码:

java 复制代码
    @Test
    public void test() {

        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        };
        r1.run();

        System.out.println("*******************************");
		// Lambda表达式重写
        Runnable r2 = () -> System.out.println("Bye World!");
        r2.run();

    }

1.3.2、语法格式二:Lambda 需要一个参数,但是没有返回值

示例代码:

java 复制代码
    @Test
    public void test2() {

        Consumer<String> c1 = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };

        c1.accept("Hello World!");
        System.out.println("*******************************");
        
        // 给定参数 String s
        Consumer<String> c2 = (String s) -> System.out.println(s);
        c2.accept("Bye World!");

    }

1.3.3、语法格式三:数据类型可以省略,因为可由编译器推断得出,称为"类型推断"

示例代码:

java 复制代码
    @Test
    public void test3() {

        // 给定参数 String s
        Consumer<String> c1 = (String s) -> System.out.println(s);
        c1.accept("Hello World!");

        System.out.println("*******************************");

        // 给定参数 s
        // 编译器推断得出参数类型
        Consumer<String> c2 = (s) -> System.out.println(s);
        c2.accept("Bye World!");

    }

1.3.4、语法格式四:Lambda若只需要一个参数时,参数的小括号可以省略

示例代码:

java 复制代码
@Test
public void test4() {
    // 给定参数 String s
    Consumer<String> c1 = (s) -> System.out.println(s);
    c1.accept("Hello World!");

    System.out.println("*******************************");

    // 给定参数 s
    // Lambda若只需要一个参数时,参数的小括号可以省略
    Consumer<String> c2 = s -> System.out.println(s);
    c2.accept("Bye World!");
}

1.3.5、语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值

示例代码:

java 复制代码
@Test
public void test5() {

    Comparator<Integer> com1 = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        }
    };

    System.out.println(com1.compare(1, 2));
    System.out.println("*************************************");

    Comparator<Integer> com2 = (o1, o2) -> {
        System.out.println(o1);
        System.out.println(o2);
        return o1.compareTo(o2);
    };
    System.out.println(com2.compare(12, 2));

}

1.3.6、语法格式六:当Lambda体只有一条语句时,若有return 与大括号,都可以省略

示例代码:

java 复制代码
@Test
public void test6() {

    Comparator<Integer> com1 = (o1, o2) -> {return o1.compareTo(o2);};

    System.out.println(com1.compare(1, 2));
    System.out.println("*************************************");

    Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2);
    System.out.println(com2.compare(12, 2));

}

2、函数式接口

2.1、什么是函数式接口

只包含一个抽象方法的接口,称为函数式接口

你可以通过Lambda表达式来创建该接口对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。

我们可以在一个接口上使用**@FunctionalInterface**注解,这样做可以检查它是否是一个函数式接口。同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。

在 java.util.function 包下定义了Java 8的丰富的函数式接口

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

2.2.1、Consumer消费者接口

Consumer<T> ------ 对类型为T的对象应用操作,包含方法:void accept(T t)

java 复制代码
    // --------------- Consumer 消费者接口 ---------------
    public void happyTime(double money, Consumer<Double> con) {
        con.accept(money);
    }

    @Test
    public void testConsumer() {
        // 匿名内部类写法
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("学习太累了,去全家便利店买了瓶矿泉水,价格为:" + aDouble);
            }
        });

        System.out.println("********************");

        // Lambda 写法
        happyTime(400, money -> System.out.println("学习太累了,去全家便利店买了瓶矿泉水,价格为:" + money));
    }

2.2.2、Supplier供给型接口

返回类型为T的对象,包含方法:T get()

java 复制代码
    // --------------- Supplier 供给型接口 ---------------
    public String getSomething(Supplier<String> sup) {
        return sup.get();
    }

    @Test
    public void testSupplier() {
        // 匿名内部类写法
        String food1 = getSomething(new Supplier<String>() {
            @Override
            public String get() {
                return "吃顿大餐";
            }
        });
        System.out.println("匿名内部类:" + food1);

        System.out.println("********************");

        // Lambda 写法
        String food2 = getSomething(() -> "喝杯奶茶");
        System.out.println("Lambda:" + food2);
    }

2.2.3、Function函数型接口

对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)

java 复制代码
   // --------------- Function 函数型接口 ---------------
    public Integer strToLen(String str, Function<String, Integer> fun) {
        return fun.apply(str);
    }

    @Test
    public void testFunction() {
        // 匿名内部类写法
        Integer len1 = strToLen("hello", new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return s.length();
            }
        });
        System.out.println("匿名内部类:字符串长度 = " + len1);

        System.out.println("********************");

        // Lambda 写法
        Integer len2 = strToLen("lambda", s -> s.length());
        System.out.println("Lambda:字符串长度 = " + len2);
    }

2.2.4、Predicate断定型接口

确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t)

java 复制代码
   // --------------- Predicate 断定型接口 ---------------
    public boolean isAdult(int age, Predicate<Integer> pre) {
        return pre.test(age);
    }

    @Test
    public void testPredicate() {
        // 匿名内部类写法
        boolean r1 = isAdult(17, new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer >= 18;
            }
        });
        System.out.println("匿名内部类:是否成年?" + r1);

        System.out.println("********************");

        // Lambda 写法
        boolean r2 = isAdult(20, age -> age >= 18);
        System.out.println("Lambda:是否成年?" + r2);
    }

3、Stream API

3.1、Stream API说明

Java 8中有两大最为重要的改变。

第一个是Lambda表达式 ;另外一个则是 Stream API

Stream API (java.util.stream)把真正的函数式编程风格引入到Java中。

这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

**使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。**也可以使用 Stream API 来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式。

3.2、为什么要使用Stream API

实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongoDB,Redis等,而这些NoSQL的数据就需要Java层面去处理。

Stream 和 Collection集合的区别:**Collection是一种静态的内存数据结构,而Stream是有关计算的。**前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过 CPU实现计算。

3.3、什么是 Stream

Stream到底是什么呢?

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

"集合讲的是数据,Stream讲的是计算!"

注意:

1、Stream自己不会存储元素。

2、Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。

3、Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

3.4、Stream 操作的三个步骤

1、创建 Stream

一个数据源(如:集合、数组),获取一个流。

2、中间操作

一个中间操作链,对数据源的数据进行处理

3、终止操作(终端操作)

一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

3.5、创建Stream对象

3.5.1、方式一:通过集合

Java8中的Collection接口被扩展,提供了两个获取流的方法:

  • default Stream<E> stream():返回一个顺序流
  • default Stream<E> parallelStream(): 返回一个并行流
java 复制代码
public void test() {
    List<Integer> list = Arrays.asList(1,2,3,4);
    // `default Stream<E> stream()`:返回一个顺序流
    Stream<Integer> stream = list.stream();
    // `default Stream<E> parallelStream()`: 返回一个并行流
    Stream<Integer> parallelStream = list.parallelStream();
}

3.5.2、方式二:通过数组

Java8中的Arrays工具类的静态方法stream()可以获取数组流:

  • static <T> Stream<T> stream(T[] array):返回一个流

重载形式,能够处理对应基本类型的数组:

  • public static IntStream stream(int[] array)
  • public static LongStream stream(long[] array)
  • public static DoubleStream stream(double[] array)
java 复制代码
@Test
public void test2() {
    int[] arr = new int[]{1,2,3,4,5};
    // 调用`Arrays`工具类的静态方法`stream()`
    IntStream stream = Arrays.stream(arr);

    Person person = new Person("Jerry",13);
    Person person1 = new Person("Carl",14);
    Person[] persons = new Person[]{person,person1};
    Stream<Person> personStream = Arrays.stream(persons);

}

3.5.3、方式三:通过Stream的of()

可以调用Stream类静态方法of(),通过显示值创建一个流。它可以接收任意数量的参数。

  • public static<T> Stream<T> of(T... values): 返回一个流
java 复制代码
@Test
public void test3() {

    Stream<Integer> stream = Stream.of(1,2,3,4);

}

3.5.4、方式四:创建无限流

可以使用静态方法 Stream.iterate()Stream.generate(),创建无限流。

  • 迭代

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

  • 生成

public static<T> Stream<T> generate(Supplier<T> s)

java 复制代码
@Test
public void test4() {
    // 遍历前10个偶数
    Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);

    // 生成十个随机数
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
}

3.6、Stream的中间操作

3.6.1、筛选与切片

filter(Predicate p)------接收 Lambda,从流中排除某些元素。

java 复制代码
        List<Person> persons = Arrays.asList(person1,person2,person3,person4,person5,person6,person7,person8);
        // filter(Predicate p) ------ 接收 Lambda,从流中排除某些元素。
        Stream<Person> personStream = persons.stream();
        // 查询年龄大于4的人
        personStream.filter(person -> person.age > 4).forEach(System.out::println);

limit(n)------截断流,使其元素不超过给定数量。

java 复制代码
// limit(n) ------ 截断流,使其元素不超过给定数量。
persons.stream().limit(3).forEach(System.out::println);

skip(n) ------ 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个则返回一个空流

java 复制代码
// skip(n) ------ 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个则返回一个空流
persons.stream().skip(6).forEach(System.out::println);

distinct() ------ 筛选,通过流所生成元素的hashCode()equals()去除重复元素

java 复制代码
        // distinct() ------ 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
        persons.add(new Person("Jerry",1));
        persons.add(new Person("Jerry",1));
        persons.add(new Person("Jerry",1));
        persons.add(new Person("Jerry",1));
        persons.add(new Person("Jerry",1));
        persons.stream().distinct().forEach(System.out::println);

3.6.2、映射

map(Function f) ------ 接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。

练习:获取姓名长度大于4的人的姓名。

java 复制代码
        // map(Function f) ------ 接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
        List<String> list = Arrays.asList("aa","bb","cc","dd","ee");
        // 通过toUpperCase将每一个元素映射为新的String
        list.stream().map(String::toUpperCase).forEach(System.out::println);

        // 练习:获取姓名长度大于4的人的姓名。
        Person person1 = new Person("Jerry",1);
        Person person2 = new Person("Carl",2);
        Person person3 = new Person("Jay",3);
        Person person4 = new Person("Cali",4);
        Person person5 = new Person("Henry",5);
        Person person6 = new Person("Jack",6);
        Person person7 = new Person("Vero",7);
        Person person8 = new Person("Fiona",8);

        List<Person> persons = new ArrayList<>(Arrays.asList(
                person1,person2,person3,person4,
                person5,person6,person7,person8
        ));
        // 通过stream的映射将原本的Person流变成了String流
        Stream<String> stringStream = persons.stream().map(Person::getName);
        stringStream.filter(name -> name.length() > 4).forEach(System.out::println);

flatMap(Function f) ------ 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

java 复制代码
    @Test
    public void test7() {
        // map(Function f) ------ 接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
        List<String> list = Arrays.asList("aa","bb","cc","dd","ee");
        // 通过map和fromStringToStream方法将String list中每个字符输出
        // 先将String都变为Stream<Character>
        Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest::fromStringToStream);
        // 再遍历5个Stream,通过Stream内部遍历再输出字符
        streamStream.forEach(stream -> stream.forEach(System.out::println));

        // flatMap(Function f) ------ 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
        // 这里比起map映射要少一个Stream嵌套
        Stream<Character> characterStream = list.stream().flatMap(StreamAPITest::fromStringToStream);
        characterStream.forEach(System.out::println);

    }

    // 将String类型转换为Stream类型
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for(char c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }

3.6.3、排序

sorted():产生一个新流,其中按自然顺序排序

java 复制代码
        // sorted(): 产生一个新流,其中按自然顺序排序
        List<Integer> list = Arrays.asList(-1,0,56,12,83,34);
        list.stream().sorted().forEach(System.out::println);

sorted(Comparator com):产生一个新流,其中按比较器顺序排序

java 复制代码
Person person1 = new Person("Jerry",1);
Person person2 = new Person("Carl",2);
Person person3 = new Person("Jay",3);
Person person4 = new Person("Cali",4);
Person person5 = new Person("Henry",5);
Person person6 = new Person("Jack",6);
Person person7 = new Person("Vero",7);
Person person8 = new Person("Fiona",8);

List<Person> persons = new ArrayList<>(Arrays.asList(
        person1,person2,person3,person4,
        person5,person6,person7,person8
));

// sorted(Comparator com): 产生一个新流,其中按比较器顺序排序
// 两种写法相同
persons.stream().sorted((o1,o2) -> o1.getAge() - o2.getAge()).forEach(System.out::println);
persons.stream().sorted((o1,o2) -> Integer.compare(o1.getAge(),o2.getAge())).forEach(System.out::println);

3.7、Stream的终止操作

3.7.1、匹配与查找

allMatch(Predicate p) ------ 检查是否匹配所有元素

java 复制代码
// 是否所有人年龄都大于0
boolean allMatch = persons.stream().allMatch(person -> person.getAge() > 0);
System.out.println(allMatch);

anyMatch(Predicate p) ------ 检查是否至少匹配一个元素

java 复制代码
// 是否有人年龄都大于8
boolean anyMatch = persons.stream().anyMatch(person -> person.getAge() > 8);
System.out.println(anyMatch );

noneMatch(Predicate p) ------ 检查是否没有匹配所有元素

java 复制代码
// 是否没有人年龄都大于8
boolean noneMatch = persons.stream().noneMatch(person -> person.getAge() > 8);
System.out.println(noneMatch);

findFirst() ------ 返回第一个元素

java 复制代码
// 返回第一个元素
Optional<Person> first = persons.stream().findFirst();
System.out.println(first);

findAny() ------ 返回当前流中的任意元素

java 复制代码
// 返回当前流中的任意元素
Optional<Person> any = persons.parallelStream().findAny();
System.out.println(any);

count() ------ 返回流中元素总数

java 复制代码
// 返回流中元素年龄大于1的总数
long count = persons.stream().filter(person -> person.getAge() > 1).count();
System.out.println(count);

max(Comparator c) ------ 返回流中最大值

java 复制代码
// 返回流中最大值,以年龄排序
Optional<Person> max = persons.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println(max);

min(Comparator c) ------ 返回流中最小值

java 复制代码
// 返回流中最小值,以年龄排序
Optional<Person> min = persons.stream().min((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println(min);

forEach(Consumer c) ------ 内部迭代(使用 Collection接口需要用户去做迭代,称为外部迭代。相反,StreamAPI 使用内部迭代------它帮你把迭代做了)

java 复制代码
// 内部迭代
persons.stream().forEach(System.out::println);

3.7.2、规约

reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回T。

java 复制代码
// 计算1-10自然数的和
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// 这里第一个参数 identity 作为初始值
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println(reduce);

reduce(BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回Optional<T>

java 复制代码
// 计算所有人的总年龄
Stream<Integer> ageStream = persons.stream().map(Person::getAge);
Optional<Integer> totalAge = ageStream.reduce(Integer::sum);
System.out.println(totalAge);

3.7.3、收集

collect(Collector c) ------ 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。

java 复制代码
// 查找年龄大于5的员工,结果返回一个List
List<Person> collect = persons.stream().filter(person -> person.getAge() > 5).collect(Collectors.toList());
System.out.println(collect);
java 复制代码
// 查找年龄大于5的员工,结果返回一个Set
Set<Person> set = persons.stream().filter(person -> person.getAge() > 5).collect(Collectors.toSet());
System.out.println(set);

4、Optional类

到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

Optional<T>类 ( java.util.Optional ) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。

4.1、创建Optional类的方法

Optional.of(T t) ------ 创建一个Optional 实例,t必须非空

java 复制代码
String str = "hello";
String nullStr = null;

// of(T t):必须传非null,传null会报错
Optional<String> opt1 = Optional.of(str);
System.out.println("of: " + opt1.get());

Optional.empty() ------ 创建一个空的Optional 实例

java 复制代码
// empty():空的Optional
Optional<String> opt2 = Optional.empty();
System.out.println("empty: " + opt2.isPresent());

Optional.ofNullable(T t) ------ t可以为null

java 复制代码
// ofNullable(T t):可以传null,安全
Optional<String> opt3 = Optional.ofNullable(nullStr);
System.out.println("ofNullable: " + opt3.isPresent());

4.2、判断Optional容器中是否包含对象

boolean isPresent() ------ 判断是否包含对象

java 复制代码
Optional<String> opt = Optional.ofNullable("hello");

// 1. isPresent() 判断是否有值
System.out.println(opt.isPresent()); // true

void ifPresent(Consumer<? super T> consumer) ------ 如有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。

java 复制代码
Optional<String> opt = Optional.ofNullable("hello");

// 2. ifPresent 有值才执行
opt.ifPresent(s -> System.out.println("值为:" + s));

4.3、获取Optional容器的对象

T get() ------ 如果调用对象包含值,返回该值,否则抛异常

java 复制代码
Optional<String> opt1 = Optional.of("Java");
Optional<String> opt2 = Optional.empty();

// 1. get() 有值返回,没值抛 NoSuchElementException
System.out.println(opt1.get());
// opt2.get(); // 运行报错

T orElse(T other) ------ 如果有值则将其返回,否则返回指定的other对象。

java 复制代码
Optional<String> opt1 = Optional.of("Java");
Optional<String> opt2 = Optional.empty();

// 2. orElse(T other) 有值返回值,无值返回 other
System.out.println(opt2.orElse("默认值"));

T orElseGet(Supplier<?extends T> other) ------ 如果有值则将其返回,否则返回由Supplier接口实现提供的对象。

java 复制代码
Optional<String> opt1 = Optional.of("Java");
Optional<String> opt2 = Optional.empty();

// 3. orElseGet(Supplier) 无值时才执行供给逻辑
System.out.println(opt2.orElseGet(() -> "动态生成的默认值"));

T orElseThrow(Supplier<? extends X> exceptionSupplier) ------ 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。

java 复制代码
Optional<String> opt1 = Optional.of("Java");
Optional<String> opt2 = Optional.empty();

// 4. orElseThrow 无值抛出自定义异常
opt2.orElseThrow(() -> new RuntimeException("值不存在"));

5、方法引用和构造器引用

5.1、方法引用

使用情景:当要传递给Lambda体操作,已经有实现的方法了,可以使用方法引用(Method References)

方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。

要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!

格式:使用操作符:: (或对象 )与方法名分隔开来。

如下三种主要使用情况:

  • 对象::实例方法名
  • 类::静态方法名
  • 类::实例方法名

5.1.1、对象::实例方法名

两个实例:

java 复制代码
// 情况一: 对象::实例方法
// Consumer中void accept(T t)
// PrintStream中void println(T t)
@Test
public void test() {
    Consumer<String> consumer = s -> System.out.println(s);
    consumer.accept("Hello");

    System.out.println("**********************************");
    // out为一个对象,有println方法
    PrintStream out = System.out;
    Consumer<String> consumer2 = out::println;
    consumer2.accept("Hello2");

}

// 情况一: 对象::实例方法
// Supplier 中的 T get()
// Person对象中的 String getName()
@Test
public void test2() {
    Person person = new Person("Aurora",13);
    Supplier<String> supplier = () -> person.getName();
    System.out.println(supplier.get());

    System.out.println("********************");
    Supplier<String> supplier2 = person::getName;
    System.out.println(supplier2.get());
}

5.1.2、类::静态方法名

两个实例:

java 复制代码
// 情况二: 类::静态方法
// Comparator 中的  int compare(T o1, T o2);
// Integer 类中的  public static int compare(int x, int y)
@Test
public void test3() {
    Comparator<Integer> comparator = (t1, t2) -> Integer.compare(t1, t2);
    System.out.println(comparator.compare(1, 2));
    System.out.println("******************************************");
    Comparator<Integer> comparator2 = Integer::compare;
    System.out.println(comparator2.compare(1, 2));
}

// 情况二: 类::静态方法
// Function<T, R> 中的 R apply(T t);
// Math 类中的 public static long round(double a)
@Test
public void test4() {
    Function<Double, Long> func = d ->  Math.round(d);
    System.out.println(func.apply(1.686));
    System.out.println("******************************");
    Function<Double, Long> function = Math::round;
    System.out.println(function.apply(2.386));
}

5.1.3、类::非静态方法名

三个实例:

java 复制代码
// 情况三: 类::实例方法(非静态方法)
// Comparator 中 int compare(T o1, T o2);
// String 中非静态的 int compareTo(String anotherString);
@Test
public void test5() {
    Comparator<String> comparator = (t1, t2) -> t1.compareTo(t2);
    System.out.println(comparator.compare("Hello", "World"));
    System.out.println("*******************************");
    Comparator<String> comparator2 = String::compareTo;
    System.out.println(comparator2.compare("Hello", "World"));
}

// 情况三: 类::实例方法(非静态方法)
// BiPredicate 中 boolean test(T t, U u);
// String 中非静态的 boolean equals(Object anObject);
@Test
public void test6() {
    BiPredicate<String, String> biPredicate = (t1, t2) -> t1.equals(t2);
    System.out.println(biPredicate.test("Hello", "World"));
    System.out.println("****************************");
    BiPredicate<String, String> biPredicate2 = String::equals;
    System.out.println(biPredicate2.test("Hello", "Hello"));
}

// 情况三: 类::实例方法(非静态方法)
// Function 中 R apply(T t);
// Person 中非静态的 String getName();
@Test
public void test7() {
    Person person = new Person("Gus",41);
    Function<Person, String> function = (p) -> p.getName();
    System.out.println(function.apply(person));
    System.out.println("******************************");
    Function<Person, String> function2 = Person::getName;
    System.out.println(function2.apply(person));
}

5.2、构造器引用和数组引用

5.2.1、构造器引用

构造器引用是方法引用的一种特殊形式,用于简化"通过构造器创建对象"的 Lambda 表达式。其基本语法为:

类名::new

构造器引用的三个实例:

java 复制代码
// 构造器引用
// 类名::new
// Supplier 中的T get() ------ 无参构造器引用(Supplier)
@Test
public void test8() {
    Supplier<Person> supplier = () -> new Person();
    System.out.println(supplier.get());
    System.out.println("******************************");
    Supplier<Person> supplier2 = Person::new;
    System.out.println(supplier2.get());
}

// Function 中的 R apply(T t); ------ 单参数构造器引用(Function)
@Test
public void test9() {
    Function<String, Person> function = (name) -> new Person(name);
    Person jimmy = function.apply("Jimmy");
    System.out.println(jimmy);
    System.out.println("******************************");
    Function<String, Person> function2 = Person::new;
    System.out.println(function2.apply("Jimmy"));
}

// BiFunction 中的 R apply(T t); ------ 多参数构造器引用(BiFunction)
@Test
public void test10() {
    BiFunction<String, Integer, Person> biFunction = (name, age) -> new Person(name, age);
    System.out.println(biFunction.apply("Steve", 25));
    System.out.println("********************************");
    BiFunction<String, Integer, Person> biFunction2 = Person::new;
    System.out.println(biFunction2.apply("Steve", 25));
}

5.2.2、数组引用

数组引用本质上也是构造器引用的一种形式,用于创建数组对象。

基本语法:

数据类型[]::new

数组引用的例子

java 复制代码
// 数组引用
// 数据类型[]::new
@Test
public void test11() {
    Function<Integer, String[]> function = (length) -> new String[length];
    String[] apply = function.apply(5);
    System.out.println(Arrays.toString(apply));
    System.out.println("*******************************");
    Function<Integer, String[]> function2 = String[]::new;
    String[] apply1 = function2.apply(10);
    System.out.println(Arrays.toString(apply1));
}
相关推荐
曹牧2 小时前
Eclipse:悬停提示(Hover)
java·ide·eclipse
oyzz1202 小时前
Spring EL 表达式的简单介绍和使用
java·后端·spring
拾贰_C2 小时前
【Google | Gemini | API | POST】怎么使用Google 的Gemini API (原生版)
开发语言·lua
iNgs IMAC3 小时前
Redis之Redis事务
java·数据库·redis
程序员小假3 小时前
向量检索的流程是怎样的?Embedding 和 Rerank 各自的作用?
java·后端
yaaakaaang3 小时前
二十二、模板方法模式
java·模板方法模式
Wmenghu3 小时前
Ubuntu手动安装jdk;Ubuntu手动安装Maven;Ubuntu手动安装RocketMQ;Ubuntu手动安装RocketMQ-Dashbo
java·linux·ubuntu
后置的猿猴3 小时前
Spring 循环依赖
java·后端·spring