07.方法引用【5种常用的::及案例展示】

52 阅读5分钟

07.方法引用【5种常用的::及案例展示】

lambda表达式冗余!!!!!

  • lambda表达式冗余!!!!!

在使用lambda表达式的时候也会出现代码冗余的情况,

比如:用lambda表达式求一个数组的和。

【案例】:lambda表达式-数组求和

public static void fun8(Consumer<int[]> consumer) {
    int[] arr = {22, 33, 44, 23, 4, 5};
    consumer.accept(arr);
}
@Test
@DisplayName("案例:统计数组中所有数据的求和")
public void test8() {
    fun8((arr) -> {
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        System.out.println(sum);
    });

Result:

131

我们使用lambda表达式写数组求和,为什么不直接写一个方法求和呢?

普通方式-方法求和:

public static void printMax(int a[]){
    int sum = 0;
    for (int i : a) {
        sum += i;
    }
    System.out.println("数组和:" + sum);
}
@Test
@DisplayName("案例:普通方式-统计数组中所有数据的求和")
public void test9() {
    int arr[] = {2,54,2,45};
    printMax(arr);
}

Result:

数组和:103

为什么要用方法引用

lambda 冗余解决方案

因为在lambda表达式中要执行的代码和我们另一个方法中的代码是一样的,这时就没有必要重写一份逻辑了,这时我们就可以使用方法引用,方法引用其实就是引用重复代码。

首先还是先定一个静态方法,计算数据的求和方法。

public static void fun8(Consumer<int[]> consumer) {
    int[] arr = {22, 33, 44, 23, 4, 5};
    consumer.accept(arr);
}
public static void printMax(int a[]){
    int sum = 0;
    for (int i : a) {
        sum += i;
    }
    System.out.println("数组和:" + sum);
}
@Test
@DisplayName("案例:方法引用")
public void test10() {
    fun8(MyTest::printMax);
}

方法引用的语法格式

::双冒号为方法引用运算符,而它所在的表达式称为方法引用

方法引用的应用场景

如果lambda表达式所要实现的方案,已经有其他的方法存在相同的方案,那么则可以使用方法引用。

常见的引用方式

方法引用在JDK8中式相当灵活的,有以下几种形式:

  • instanceName::methodName 对象::方法名
  • ClassName::staticMethodName 类名::静态方法
  • ClassName::methodName 类名::普通方法
  • ClassName::new 类名::new 调用的构造器
  • TypeName[]::new String[]::new 调用数组的构造器

常用的引用方式-细节

对象名::方法名【最常见的一种用法】

如果一个类中已经存在了一个成员方法,则可以通过对象名引用成员方法。

先看lambda表达式中如何获取时间对象的毫秒差:

@Test
@DisplayName("lambda表达式获取时间")
public void test11() {
    Date date = new Date();
    System.out.println("返回的日期对象是date = " + date);

    Supplier<Long> supplier = () ->{return date.getTime();};
    System.out.println("时间差毫秒为:" + supplier.get());
}

使用方法引用 改进:

@Test
@DisplayName("使用方法引用优雅的获取时间差-lambda表达式获取时间")
public void test12() {
    Date date = new Date();
    System.out.println("返回的日期对象是date = " + date);

    Supplier<Long> supplier = date::getTime;
    System.out.println("时间差毫秒为:" + supplier.get());
}

对象名::方法名 ⚠️:

  • 方法引用的方法,参数要和接口中的抽象方法的参数一样。
  • 当接口抽象方法有返回值时,被引用的方法也必须有返回值。

类名::静态方法【也是比较常用的】

先看lambda表达式获取当前系统时间的信息的【案例】

@Test
@DisplayName("lambda表达式获取当前系统时间的信息")
public void test13() {
   Supplier<Long> supplier = ()->{
       return System.currentTimeMillis();
   };
    System.out.println(supplier.get());
}

image.png

紧接着,我们使用方法引用优雅写代码:

@Test
@DisplayName("方法引用:类名::静态方法名")
public void test14() {
    Supplier<Long> supplier = System::currentTimeMillis;
    System.out.println(supplier.get());
}

类名::引用实例方法

java面向对象,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数作为方法的调用者。

lambda表达式案例:传入一个String,求长度:

@Test
@DisplayName("lambda表达式案例:传入一个String,求长度")
public void test15() {
   Function<String, Integer> function = (s) ->{
       return s.length();
   };
    System.out.println(function.apply("Hello, world!"));
}

优雅的使用 方法引用改造:类名::引用实例方法

@Test
@DisplayName("优雅的使用 `方法引用`改造:lambda表达式案例:传入一个String,截取")
public void test16() {

    Function<String, Integer> f = String::length;
    Integer le = f.apply("hello");
    System.out.println(le);

    BiFunction<String,Integer,String> function =String::substring;
    String msg = function.apply("Hello, world!", 3);
    System.out.println("截取后"+msg);
}

Result:

5
截取后lo, world!

image.png

image.png

类名::构造器

由于构造器的名称和类名完全一致,所以构造器引用使用::new 的格式使用。

【案例】lambda表达式案例:获取Person对象:

package com.lambda.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author:kaiyang.cui
 * @Package:com.lambda.domain
 * @Project:jdk8
 * @name:Person
 * @Date:2023/4/2 下午3:58
 * @Filename:Person
 * @Description:Person实体类
 * @Version:1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {

    private String name;

    private Integer age;

    private Integer height;
}
@Test
@DisplayName("lambda表达式案例:获取Person对象")
public void test17() {
    Supplier<Person> supplier = ()->{return  new Person();};
    Person person = supplier.get();
    System.out.println("person = " + person);
}

Result:

person = Person(name=null, age=null, height=null)

【案例】优雅的改造lambda表达式获取Person对象:

@Test
@DisplayName("优雅的改造lambda表达式获取Person对象")
public void test18() {
    Supplier<Person> supplier = Person::new;
    Person person = supplier.get();
    System.out.println("person = " + person);
}

Result:

person = Person(name=null, age=null, height=null)

上面的Person对象是null,我们来改造赋值:

package com.lambda.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author:kaiyang.cui
 * @Package:com.lambda.domain
 * @Project:jdk8
 * @name:Person
 * @Date:2023/4/2 下午3:58
 * @Filename:Person
 * @Description:Person实体类
 * @Version:1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {

    private String name;

    private Integer age;

//    private Integer height;
}
@Test
@DisplayName("优雅的改造lambda表达式获取Person对象,赋值版")
public void test19() {
    BiFunction<String,Integer,Person> function = Person::new;
    System.out.println(function.apply("张三",22));
}

Result:

Person(name=张三, age=22)

数组::构造器

数组是怎么构造出来的 ?

【案例】lambda 返回数组的长度

@Test
@DisplayName("lambda 返回数组的长度")
public void test20() {
    Function<Integer, String[]> function = (len)->{
        return  new String[len];
    };
    String[] arr = function.apply(3);
    System.out.println(arr.length);
}

【案例改造】方法引用 优雅实现:lambda 返回数组的长度

@Test
@DisplayName("方法引用 优雅实现:lambda 返回数组的长度")
public void test21() {
    Function<Integer, String[]> function = String[]::new;
    String[] arr = function.apply(3);
    System.out.println("arr的长度是 = " + arr.length);
}

总结

方法引用是对lambda表达式符合特定情况下对一种缩写方式,使得lambda表达式更加精简,也可以理解为lambda表达式的缩写形式

注意: 方法引用只能引用已经存在的方法。