方法的引用
# 方法引用
# 1. 方法引用概述
方法引用是 Lambda 表达式的简化形式,通过方法的名字直接指向现有方法。方法引用使代码更加简洁、直观,并且可以避免重复编写已有的逻辑。可以看作是对 Lambda 表达式的进一步优化,当 Lambda 体中的内容是对现有方法的直接调用时,方法引用是一种更优雅的方式。
- 方法引用的本质:方法引用和 Lambda 表达式一样,都是通过实现一个函数式接口的抽象方法来完成任务。函数式接口只有一个抽象方法,这使得方法引用可以匹配并替代 Lambda 表达式的实现。
- 方法引用需要一个接口对象接收:因为方法引用本质上是在实现函数式接口的抽象方法,所以它需要一个函数式接口对象接收,就像 Lambda 表达式一样。这个函数式接口的抽象方法决定了方法引用所指向方法的参数和返回值。
- 参数和返回值的匹配:在方法引用中,被引用的方法的参数和返回值必须与函数式接口的抽象方法一致。正因为这一点,方法引用可以作为 Lambda 表达式的替代,并且让代码更加简洁。
# 2. 使用场景
当 Lambda 表达式中的操作已经有现成的方法可以直接使用,并且该方法与 Lambda 所实现的函数式接口的抽象方法在参数列表和返回值类型上完全一致时,可以使用方法引用。
# 3. 方法引用的使用格式
方法引用有三种常见格式,具体使用方式如下:
- 对象::实例方法
- 类::静态方法
- 类::实例方法
# 4. 方法引用的使用要求
要使用方法引用,必须满足以下条件:
函数式接口的抽象方法与方法引用的方法在参数列表和返回值类型上完全一致。 这意味着方法引用必须能够匹配接口中唯一的抽象方法,确保其形参和返回值一致。
对于
类::实例方法
的情况,函数式接口的第一个参数应当是调用方法的对象,第二个参数是方法的入参(或无参数)。 例如,Comparator<String>
接口中的compare(T o1, T o2)
方法可以通过String::compareTo
引用实现,因为第一个参数作为调用者,第二个参数作为入参。
# 5. 方法引用的使用示例
# 1. 对象::实例方法
使用场景: 当 Lambda 表达式中的操作是一个现有对象的实例方法时,可以使用这种方法引用。
示例:
import java.util.function.Consumer;
public class MethodRefExample1 {
public static void main(String[] args) {
// 使用 Lambda 表达式实现 Consumer 接口
Consumer<String> lambdaConsumer = (str) -> System.out.println(str);
lambdaConsumer.accept("Hello, Lambda!");
// 使用方法引用实现 Consumer 接口
PrintStream printStream = System.out; // 获取输出流对象
Consumer<String> methodRefConsumer = printStream::println; // 方法引用,引用 System.out 对象的 println 方法
methodRefConsumer.accept("Hello, Method Reference!"); // 输出 "Hello, Method Reference!"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
函数式接口抽象方法与引用方法的对比:
Consumer<String>
接口的void accept(T t)
方法:- 参数:
String t
- 返回值:
void
- 参数:
- 被引用方法
PrintStream.println(String x)
:- 参数:
String x
- 返回值:
void
- 参数:
两者的参数列表和返回值类型完全一致,因此可以使用方法引用。
使用要求:
- 方法引用与函数式接口的抽象方法的参数和返回值类型必须一致。
适用场景: 打印日志、输出信息等。
# 2. 类::静态方法
使用场景: 当 Lambda 表达式中的操作是调用一个静态方法时,可以使用这种方法引用。
示例:
import java.util.Comparator;
public class MethodRefExample2 {
public static void main(String[] args) {
// 使用 Lambda 表达式实现 Comparator 接口
Comparator<Integer> lambdaComparator = (x, y) -> Integer.compare(x, y);
System.out.println("Lambda Comparator: " + lambdaComparator.compare(3, 7)); // 输出:-1
// 使用方法引用实现 Comparator 接口
Comparator<Integer> methodRefComparator = Integer::compare; // 引用 Integer 类的 compare 静态方法
System.out.println("Method Reference Comparator: " + methodRefComparator.compare(3, 7)); // 输出:-1
}
}
2
3
4
5
6
7
8
9
10
11
12
13
函数式接口抽象方法与引用方法的对比:
Comparator<Integer>
接口的int compare(T o1, T o2)
方法:- 参数:
Integer o1, Integer o2
- 返回值:
int
- 参数:
- 被引用方法
Integer.compare(int x, int y)
:- 参数:
int x, int y
- 返回值:
int
- 参数:
两者的参数列表和返回值类型完全一致,因此可以使用方法引用。
使用要求:
- 静态方法的参数和返回值类型与函数式接口的抽象方法一致。
适用场景: 数值比较、排序操作等。
# 3. 类::实例方法
使用场景: 在我们实现的函数式接口的抽象方法中,可能需要调用其他现有的方法。如果这个抽象方法的第一个参数是我们内部要调用的其他方法的调用者,第二个参数是我们内部调用的方法的参数(或者无参数),那么我们可以直接使用方法引用来简化代码。
示例:
import java.util.Comparator;
public class MethodRefExample3 {
public static void main(String[] args) {
// 使用匿名内部类实现 Comparator 接口
Comparator<String> traditionalComparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
System.out.println("传统方式比较结果:" + traditionalComparator.compare("abc", "abd")); // 输出:-1
// 使用 Lambda 表达式实现 Comparator 接口
Comparator<String> lambdaComparator = (s1, s2) -> s1.compareTo(s2);
System.out.println("Lambda 表达式比较结果:" + lambdaComparator.compare("abc", "abd")); // 输出:-1
// 使用方法引用实现 Comparator 接口
Comparator<String> methodRefComparator = String::compareTo; // 引用 String 类的 compareTo 方法
System.out.println("方法引用比较结果:" + methodRefComparator.compare("abc", "abd")); // 输出:-1
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
函数式接口抽象方法与引用方法的对比:
Comparator<String>
接口的int compare(T o1, T o2)
方法:- 参数:
String o1, String o2
- 返回值:
int
- 参数:
- 被引用方法
String.compareTo(String anotherString)
:- 参数:
String anotherString
- 返回值:
int
- 参数:
在这个抽象方法的实现中:
o1
是内部调用的compareTo
方法的调用者。o2
是传递给compareTo
方法的参数。
重要理解点
- 只有当我们要实现的函数式接口方法中,内部调用了其他方法,并且该调用的第一个参数是方法的调用者,第二个参数是传递的参数时,才能使用这种方法引用。
- 在这种情况下,Java 编译器可以自动推断出调用关系,我们可以直接使用类名来引用实例方法,而不需要显式地实例化一个对象。
使用要求:
- 函数式接口的第一个参数是方法的调用者,第二个参数是方法的入参(或没有入参)。
- 方法引用的参数和返回值类型必须与函数式接口的抽象方法一致。
适用场景: 字符串比较、对象比较等。
# 6. 方法引用的总结和建议
- 何时使用方法引用: 当 Lambda 表达式中的操作已经有现成的方法实现,并且该方法与函数式接口的抽象方法在参数列表和返回值类型上完全一致时,使用方法引用是更简洁的选择。
- 如何选择: Lambda 表达式适用于自定义的逻辑或复杂操作,而方法引用适用于直接调用已有方法的情况。两者可以互相替代,但在简单场景中,方法引用更为直观。
通过掌握方法引用的三种常见格式,你可以在实际开发中选择最优的表达方式,使代码更加简洁、易读。