Java——方法的引用(03)

一、概念

  • 函数式接口的实现除了有Lambda表达式外,还有方法引用、构造器引用和数组引用的实现;
  • 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个对象(实例);
  • 方法引用是一种引用方法的轻量级语法;

Labmda表达式是函数式接口中抽象方法的实现

如果已经存在一个实现了Lambda表达式的方法,那么就不需要编写Lambda表达式,而直接使用这个已经存在的方法,这称之为方法引用

方法引用:引用(使用)已经存在的方法

Lambda表达式用于实现函数式接口中抽象方法 - 方法

如果已经存在某个方法可以实现函数式接口中的抽象方法,那么我们就不需要定义Lambda表达式,而直接引用已经存在的方法即可。

二、使用场景

在Lambda表达式中,如果Lambda体的操作,已经有实现的方法了,那么就可以使用方法引用

三、要求

函数式接口的抽象方法的参数列表,必须与方法引用的方法的参数列表保持一致,而返回值类型原则上也需要保持一致。

  • 如果抽象方法的返回值为void,而方法引用的方法有返回值,则忽略;
  • 抽象方法的返回值类型(如:double)必须能兼容方法引用的方法返回值类型(如:int)。
1
2
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y) ;
Comparator<Integer> comparator = Integer::compare;

四、语法

  • 使用操作符 “::” 将类(或对象)与方法名分割开来(是已经实现好的方法名及其所在的类或对象);
  • 可以看成左边的类或对象是调用者,右边的方法是被调用者。

以下是六种不同类型的方法引用:

类型例子
静态方法的引用类名::静态方法
成员方法的引用对象::成员方法
特定类型方法的引用类名::成员方法
构造器引用类名::new
数组引用数据类型[]::new
父类方法引用、本类方法引用super::成员方法this::成员方法

五、案例

1、成员方法的引用

2、静态方法的引用

第一:定义函数式接口

1
2
3
4
5
6
7
8
9
10
/**
 * 问候接口 - 函数式接口
 *
 * @Date 2023-04-07
 * @Author zqx
 */
@FunctionalInterface
public interface IWenHou {
    void sayHello(String name);
}

第二:用户对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
 * 用户
 *
 * @Date 2023-04-07
 * @Author zqx
 */
public class User {

    /**
     * 函数式接口作为方法参数的使用
     *
     * @param name
     * @param iWenHou
     */
    public void wenHao(String name, IWenHou iWenHou) {
        iWenHou.sayHello(name);
    }

    /**
     * 问候 - 早上好 - 成员方法
     *
     * @param name
     */
    public void goodMorning(String name) {
        System.out.println("早上好," + name);
    }

    /**
     * 问候 - 下午好 - 静态方法
     *
     * @param name
     */
    public static void goodAfternoon(String name) {
        System.out.println("下午好," + name);
    }

    /**
     * 问候 - 晚上好 - 静态方法 - 注意:此方法与函数式接口的方法不匹配
     *
     * @return
     */
    public static int goodNight() {
        System.out.println("晚安");
        return 8;
    }
}

第三:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * 成员方法的引用、静态方法的引用
 *
 * @Date 2023-04-07
 * @Author zqx
 */
public class MainTest {

  public static void main(String[] args) {
    // 实例化用户对象
    User user = new User() ;

    // 1.Lambda表达式 - void sayHello(String name);
    user.wenHao("李四",(name)->System.out.println("你好呀," + name));

    // 2.方法引用 - 引用已经存在的方法,代替 Lambda 表达式,从而实现函数式接口中的抽象方法
    // 2.1 引用静态方法 - 类名::静态方法
    user.wenHao("王五",User::goodAfternoon);

    // 2.2 引用成员方法 - 对象名::成员方法
    user.wenHao("赵六",user::goodMorning);

    // 错误 - 引用的方法不匹配函数式接口中的抽象方法
    // user.wenHao("赵六",User::goodNight);
  }
}

3、父类方法引用

4、本类方法引用

第一:定义函数式接口

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 函数式接口 - 移动接口
 *
 * @Date 2023-04-07
 * @Author zqx
 */
public interface IMove {
    /**
     * 移动
     */
    void move();
}

第二:定义父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * 父类
 *
 * @Date 2023-04-07
 * @Author zqx
 */
public class Animal {
    /**
     * 跑
     */
    public void run() {
        System.out.println("跑");
    }
}

第三:定义子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * 子类
 *
 * @Date 2023-04-07
 * @Author zqx
 */
public class Lion extends Animal {
    /**
     * 函数式接口作为方法参数的使用 - 移动行为
     *
     * @param im
     */
    public void move(IMove im) {
        im.move();
    }

    /**
     * 吃
     */
    public void eat() {
        System.out.println("吃");
    }

    /**
     * 捕食 - 动起来 - 调用 move 方法
     * 1)匿名内部类实现
     */
    public void buShi1() {
        move(new IMove(){
            @Override
            public void move() {
                Lion.super.run();
            }
        });
    }

    /**
     * 捕食 - 动起来 - 调用 move 方法
     * 2)Lambda表达式实现 - void move();
     */
    public void buShi2() {
        move(()->{
            super.run();
        }) ;
    }

    /**
     * 捕食 - 动起来 - 调用 move 方法
     * 3)方法引用 - void move();
     */
    public void buShi3() {
        // 引用父类的方法 - super::方法名称
        move(super::run) ;
    }

    /**
     * 捕食 - 动起来 - 调用 move 方法
     * 3)方法引用 - void move();
     */
    public void buShi4() {
        // 引用本类的方法 - this::方法名称
        move(this::eat) ;
    }
}

第四:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * @Date 2023-04-07
 * @Author zqx
 */
public class MainTest {
  public static void main(String[] args) {
    Lion xb = new Lion();

    xb.buShi1();
    xb.buShi2();
    xb.buShi3();
    xb.buShi4();
  }
}

5、特定类型方法的引用

在Lambda表达式中,如果参数列表中的第一个参数是成员方法的调用者,而第二个参数是成员方法的参数时,则可以使用特定类型引用(类名::成员方法)

1
2
3
4
5
6
7
8
9
10
// 第一个参数x是成员方法equals的调用者
// 第二个参数y是成员方法equals的参数
BiPredicate<String,String> bl1 = (x,y) -> x.equals(y) ;

// 满足上面的条件,因此可以使用特定类型的方法引用
BiPredicate<String,String> bl2 = String::equals ;


System.out.println(bl1.test("aa", "aa"));
System.out.println(bl2.test("cc", "cc"));

6、构造方法的引用

第一:定义学生对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
 * 学生
 *
 * @Date 2023-04-06
 * @Author zqx
 */
public class Student {
    /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return this.name + "-" + this.age;
    }
}

第二:定义函数式接口

1
2
3
4
5
6
7
8
9
10
/**
 * 构造学生对象的工具类 - 基于 namt 和 age ,实例化 Student 对象,并返回
 *
 * @Date 2023-04-07
 * @Author zqx
 */
@FunctionalInterface
public interface StudentBuilder {
  Student create(String name,int age) ;
}

第三:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
 * @Date 2023-04-07
 * @Author zqx
 */
public class MainTest {

  /**
     * 构造学生对象 - 函数式接口作为方法参数的使用
     *
     * @param name
     * @param age
     * @param sb
     * @return
     */
  private static Student build(String name,
                               int age,
                               StudentBuilder sb) {
    return sb.create(name, age);
  }

  public static void main(String[] args) {
    // 1.匿名内部类
    Student stu1 = build("张三", 18, new StudentBuilder() {
      @Override
      public Student create(String name, int age) {
        // 调用构造方法
        return new Student(name,age);
      }
    });
    System.out.println(stu1);

    // 2.Lambda表达式实现 - Student create(String name,int age) ;
    Student stu2 = build("李四",28,(name,age)->new Student(name,age)) ;
    System.out.println(stu2);

    // 3.构造方法的引用 - 类名::new
    Student stu3 = build("王五",38,Student::new) ;
    System.out.println(stu3);
  }
}

7、数组引用

第一:定义函数式接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * 创建指定类型的数组的工具类
 *
 * @Date 2023-04-07
 * @Author zqx
 */
@FunctionalInterface
public interface ArrayBuilder<T> {
  /**
     * 创建一个指定长度T类型的数组
     *
     * @param len
     * @return
     */
  T[] create(int len);
}

第二:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
 * 测试
 *
 * @Date 2023-04-07
 * @Author zqx
 */
public class MainTest {
  /**
     * 构造一个指定长度的字符串数组
     *
     * @param len
     * @param ab
     */
  public static String[] build(int len, ArrayBuilder<String> ab) {
    return ab.create(len);
  }

  public static void main(String[] args) {
    // 1.通过匿名内部类实现
    String[] arr1 = build(3,new ArrayBuilder<String>(){
      @Override
      public String[] create(int len) {
        return new String[len];
      }
    }) ;

    arr1[0] = "aa" ;
    arr1[1] = "bb" ;
    arr1[2] = "cc" ;

    System.out.println(Arrays.toString(arr1));

    // 2.通过 Lambda表达式 实现 - T[] create(int len);
    String[] arr2 = build(3,(len)->new String[len]) ;
    arr2[0] = "ee" ;
    arr2[1] = "ff" ;
    arr2[2] = "gg" ;
    System.out.println(Arrays.toString(arr2));

    // 3.通过 方法引用 实现 - 数据类型[]::new
    String[] arr3 = build(3,String[]::new) ;
    arr3[0] = "hh" ;
    arr3[1] = "ii" ;
    arr3[2] = "jj" ;
    System.out.println(Arrays.toString(arr3));

  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值