Lambda表达式的方法引用
Lambda表达式的方法引用
CAMELLIALambda表达式的方法引用
方法引用其实就是把一个已经存在的方法拿来用,不需要再写一遍代码。可以理解为:“别写重复的东西,直接拿来用”。比如,如果你要做一件事情,而某个方法已经写好了,那你就直接用它的名字就行,不用再写一个lambda表达式。
静态方法引用
想象你要把列表里的每个数字都打印出来,原来你可能会这样写:1
list.forEach(n -> System.out.println(n));
这其实就是每次把
n
传到System.out.println()
方法里。
但println
这个方法已经存在了,你可以直接用它的名字,像这样:1
list.forEach(System.out::println);
这样代码就更简洁了!不需要重复地写
n ->
了,因为Java知道要用println
这个方法。实例方法引用
假设你有一个对象apple
,它有一个方法叫eat()
。原来你可能会这么写:1
list.forEach(item -> apple.eat(item));
但其实
apple.eat()
已经写好了,所以你可以这样直接引用:1
list.forEach(apple::eat);
类方法引用
如果你有一个方法需要调用列表中每个对象的某个功能,比如字符串转换成大写:1
list.forEach(str -> str.toUpperCase());
因为
toUpperCase()
这个方法已经写好了,你可以直接用它的名字:1
list.forEach(String::toUpperCase);
构造方法引用
当你需要创建对象时,像这样:1
Supplier<List<String>> supplier = () -> new ArrayList<>();
new ArrayList()
这个构造器已经写好了,所以可以直接用方法引用:1
Supplier<List<String>> supplier = ArrayList::new;
当lambda表达式只是简单调用一个已有方法时,就可以用方法引用。
用方法引用会让代码更简洁、少重复,不需要每次都写lambda表达式。
在Lambda表达式的方法引用中,主要有实例方法引用、静态方法引用、特殊方法引用和构造方法引用、数组引用这五种情况
1. 实例方法引用
实例方法引用是指使用某个对象的实例方法,而不需要显式地写出lambda表达式。用实例方法引用,可以让代码更加简洁。当你用实例方法引用时,它会自动把参数传给该方法,前提是这个方法已经写好了,并且适合传入的参数。
1.1 实例方法引用的使用
假设我们有一个对象apple
,它有一个eat()
方法,用来吃水果。
1 | class Apple { |
1.1.1 使用lambda表达式
原本可能会用lambda表达式来遍历一个水果列表,并调用apple.eat()
:
1 | Apple apple = new Apple(); |
1.1.2 使用实例方法引用:
既然apple.eat()
已经存在,我们可以直接引用它的方法名:
1 | fruits.forEach(apple::eat); // 实例方法引用 |
这两种写法的效果完全一样,但使用方法引用的代码更简洁!
1.2 什么时候可以用实例方法引用?
已有的实例方法:你已经有一个对象(比如上面的
apple
),并且你想对列表中的每一项调用这个对象的某个方法。方法参数匹配:lambda表达式中的参数要和实例方法的参数一致。例如,
fruits.forEach(apple::eat)
中,forEach
会把fruits
中的每个元素传递给apple.eat()
,而eat
刚好是接受一个String
参数。
1 | package com.camellia.lambda; |
1.3 实例方法引用的格式:
1 | 实例对象::方法名 |
注意:方法引用等价于lambda表达式,也就是:
1 | apple::eat // 等价于 (fruit) -> apple.eat(fruit) |
2. 静态方法引用
静态方法引用是指直接引用类中的静态方法,而不需要通过对象调用。静态方法引用使代码更加简洁,特别是在lambda表达式中,静态方法可以直接作为逻辑处理的实现。
静态方法引用的格式
1 | 类名::静态方法名 |
2.1 静态方法引用的使用
假设有一个类MathUtils
,其中有一个静态方法doubleNumber()
,用于将数字乘以2:
1 | class MathUtils { |
2.1.1 使用lambda表达式
将一个数字列表中的每个数字都乘以2,可能会用lambda表达式来写:
1 | List<Integer> numbers = Arrays.asList(1, 2, 3, 4); |
2.1.2 使用静态方法引用
因为doubleNumber()
是一个静态方法,你可以直接用方法引用来简化代码:
1 | numbers.stream().map(MathUtils::doubleNumber).forEach(System.out::println); |
2.2 什么时候可以用静态方法引用?
有静态方法可以使用:当你已经有一个现成的静态方法,并且这个静态方法可以被重用时,比如上面的
MathUtils.doubleNumber
。参数匹配:lambda表达式中的参数要和静态方法的参数一致。在上面的例子中,
stream().map()
会把数字传递给MathUtils.doubleNumber()
,它正好接受一个int
参数并返回int
。
2.3 常见场景
工具类中的静态方法:静态方法引用通常用于像
Math
或Collections
这样的工具类,因为它们通常包含常用的、与实例无关的逻辑。例如,使用
Math.max()
来获取两个数中的最大值:1
BinaryOperator<Integer> max = Math::max;
1
2// 使用 lambda 表达式的等效写法
BinaryOperator<Integer> max = (a, b) -> Math.max(a, b);转换、计算逻辑:如果你有一些转换或计算逻辑,并且这些逻辑已经通过静态方法实现,可以使用静态方法引用。
例如,使用
Integer.parseInt()
将字符串转换为数字:1
Stream.of("1", "2", "3").map(Integer::parseInt).forEach(System.out::println);
3. 特定类型的任意对象的实例方法引用(通过参数对象调用实例方法)
在Lambda表达式的方法体中,通过参数对象调用实例方法就是方法的第一个形参来调用指定的某个“实例方法”。
接口方法的第一个参数就是你用来调用实例方法的对象。接口方法中除第一个参数外的其他参数,必须与实例方法的参数列表一一对应。
3.1 特定类型的任意对象的实例方法引用的使用
1 | // 方式一:使用匿名内部类来实现 |
1 | // 方式一:使用匿名内部类来实现 |
3.1.1 使用Lambda表达式
1 | // 方式二:使用Lambda表达式来实现 |
1 | // 方式二:使用Lambda表达式来实现 |
3.1.2 使用方法引用来
1 | // 方式三:使用方法引用来实现 |
1 | // 方式三:使用方法引用来实现 |
4. 构造方法引用
允许你使用类的构造方法来创建对象,构造方法引用是一个简洁的方式来传递构造函数作为参数。
4.1 构造方法引用的使用
构造方法引用的基本语法是:
1 | ClassName::new |
其中,ClassName
是想要引用构造方法的类名。
- 假设有一个
Person
类,其构造方法如下:
1 | public class Person { |
- 有一个接口
PersonFactory
,它有一个createPerson
方法:
1 |
|
4.1.1 使用 lambda 表达式
1 | public class Main { |
4.1.2 使用构造方法引用
1 | public class Main { |
4.2 什么时候可以用构造方法引用?
- 已有的构造方法:已经有一个类及其构造方法,并且想用这个构造方法来创建对象。
- 参数匹配:构造方法引用的参数需要与接口的抽象方法参数匹配。例如,
PersonFactory
接口的create
方法需要一个String
类型的参数,而Person
类的构造方法也正好接受一个String
参数。
4.3 使用场景
1. 工厂模式:可以用来创建对象的工厂方法。
2. 流处理:在 Java Streams API 中,可以用构造方法引用来创建对象。例如:
1 | List<String> names = Arrays.asList("John", "Jane", "Jack"); |
这里,Person::new
是构造方法引用,它将 names
列表中的每个名字映射到一个新的 Person
对象。
构造方法引用提供了一种更简洁的方式来传递构造方法,同时保持代码的可读性和简洁性。
5. 数组引用
数组引用是一种方法引用形式,用于创建指定类型的数组。在 Java 8 中,数组引用提供了一种简洁的方式来创建数组,尤其是在使用 lambda 表达式时。通过数组引用,可以在 lambda 表达式的方法体中创建并返回一个指定类型的数组。
5.1 数组引用的语法
语法如下:
1 | 数组类型::new |
5.2 数组引用的特点
- 要求:重写的方法必须有且只有一个
int
类型的参数,该参数用来设置数组的空间长度。 - 返回值类型:重写方法的返回值类型必须与创建的数组类型一致。
5.3 数组引用的使用
假设要创建一个 Function<Integer, int[]>
对象,并使用数组引用来创建不同长度的 int
数组。以下是三种实现方式:
5.3.1 使用匿名内部类
1 | import java.util.Arrays; |
5.3.2 使用 Lambda 表达式
1 | import java.util.Arrays; |
5.3.3 使用数组引用
1 | import java.util.Arrays; |