目录
在 Java 8 中引入的方法引用(Method Reference)是 Lambda 表达式的一种简化形式,它允许我们直接引用已存在的方法,从而使代码更加简洁易读。
一、什么是方法引用
方法引用是用来直接访问类或实例的已经存在的方法或构造方法。它提供了一种引用而不执行方法的方式,语法上表现为 ::
双冒号操作符。
方法引用的主要作用是简化 Lambda 表达式,当 Lambda 表达式只是调用一个已存在的方法时,就可以使用方法引用来替代。
二、方法引用的分类及示例
1. 静态方法引用
静态方法引用的语法为:类名::静态方法名
当 Lambda 表达式的参数列表和返回值类型与某个静态方法的参数列表和返回值类型一致时,可以使用静态方法引用。
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class StaticMethodReferenceExample {
// 定义一个静态方法
public static String convertToUpperCase(String str) {
return str.toUpperCase();
}
public static void main(String[] args) {
List<String> names = Arrays.asList("alice", "bob", "charlie");
// 使用Lambda表达式
names.stream()
.map(str -> StaticMethodReferenceExample.convertToUpperCase(str))
.forEach(System.out::println);
// 使用静态方法引用简化
names.stream()
.map(StaticMethodReferenceExample::convertToUpperCase)
.forEach(System.out::println);
}
}
2. 引用其他类的成员方法
当我们需要引用另一个类的实例方法时,语法为:实例对象::实例方法名
import java.util.Arrays;
import java.util.List;
public class OtherClassMethodReference {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 创建一个字符串处理工具类的实例
StringProcessor processor = new StringProcessor();
// 使用Lambda表达式
names.stream()
.map(name -> processor.formatName(name))
.forEach(System.out::println);
// 使用其他类的成员方法引用
names.stream()
.map(processor::formatName)
.forEach(System.out::println);
}
}
// 字符串处理工具类
class StringProcessor {
// 成员方法:格式化名称,使首字母大写,其余小写
public String formatName(String name) {
if (name == null || name.isEmpty()) {
return name;
}
return name.substring(0, 1).toUpperCase() +
name.substring(1).toLowerCase();
}
}
3. 引用本类或父类的成员方法
在当前类中引用自己的方法或父类的方法,语法为:this::方法名
或 super::方法名
import java.util.Arrays;
import java.util.List;
// 父类
class Parent {
// 父类的方法
public void printMessage(String message) {
System.out.println("Parent: " + message);
}
}
// 子类
public class ThisSuperMethodReference extends Parent {
// 子类重写父类方法
@Override
public void printMessage(String message) {
System.out.println("Child: " + message);
}
// 子类自己的方法
public String addPrefix(String str) {
return "Prefix: " + str;
}
public void process() {
List<String> messages = Arrays.asList("Hello", "World");
// 引用本类的方法
messages.stream()
.map(this::addPrefix)
.forEach(this::printMessage);
// 引用父类的方法
messages.stream()
.map(msg -> "From super: " + msg)
.forEach(super::printMessage);
}
public static void main(String[] args) {
new ThisSuperMethodReference().process();
}
}
4. 引用构造方法
构造方法的引用语法为:类名::new
,用于创建类的实例。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class Person {
private String name;
// 构造方法
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
}
public class ConstructorReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 使用Lambda表达式创建Person对象
List<Person> people1 = names.stream()
.map(name -> new Person(name))
.collect(Collectors.toList());
System.out.println(people1);
// 使用构造方法引用简化
List<Person> people2 = names.stream()
.map(Person::new) // 等价于name -> new Person(name)
.collect(Collectors.toList());
System.out.println(people2);
}
}
5. 类名引用成员方法
这种形式比较特殊,语法为:类名::实例方法名
。当 Lambda 表达式的第一个参数是实例方法的调用者,而其余参数作为该方法的参数时,可以使用这种形式。
import java.util.Arrays;
import java.util.List;
public class ClassNameMethodReference {
public static void main(String[] args) {
List<String> names = Arrays.asList("apple", "Banana", "cherry", "Date");
// 排序字符串,忽略大小写
// Lambda表达式形式:(s1, s2) -> s1.compareToIgnoreCase(s2)
// 可以简化为:String::compareToIgnoreCase
names.sort(String::compareToIgnoreCase);
names.forEach(System.out::println);
// 另一个例子:检查字符串是否包含某个字符
List<String> words = Arrays.asList("hello", "world", "java", "stream");
char target = 'a';
long count = words.stream()
.filter(word -> word.contains(String.valueOf(target)))
.count();
System.out.println("包含字母 '" + target + "' 的单词有 " + count + " 个");
// 使用类名引用成员方法的形式
count = words.stream()
.filter(String::isEmpty) // 检查字符串是否为空
.count();
System.out.println("空字符串的数量: " + count);
}
}
6. 引用数组的构造方法
数组构造方法的引用语法为:数组类型[]::new
,它接收一个 int 参数作为数组长度。
import java.util.Arrays;
import java.util.function.Function;
public class ArrayConstructorReference {
public static void main(String[] args) {
// 使用Lambda表达式创建指定长度的字符串数组
Function<Integer, String[]> stringArrayCreator1 = length -> new String[length];
String[] array1 = stringArrayCreator1.apply(5);
System.out.println("数组1的长度: " + array1.length);
// 使用数组构造方法引用
Function<Integer, String[]> stringArrayCreator2 = String[]::new;
String[] array2 = stringArrayCreator2.apply(10);
System.out.println("数组2的长度: " + array2.length);
// 实际应用示例:将列表转换为数组
String[] fruits = {"apple", "banana", "cherry"};
String[] copiedArray = Arrays.stream(fruits)
.toArray(String[]::new);
System.out.println("复制的数组: " + Arrays.toString(copiedArray));
}
}
三、方法引用的使用场景
方法引用并非在所有情况下都比 Lambda 表达式更好,以下是适合使用方法引用的场景:
- 当 Lambda 表达式仅调用一个已存在的方法时
- 方法名本身具有描述性,能增强代码可读性时
- 希望代码更加简洁紧凑时
四、总结
本文介绍了六种常见的方法引用类型:
- 静态方法引用:
类名::静态方法名
- 引用其他类的成员方法:
实例对象::实例方法名
- 引用本类或父类的成员方法:
this::方法名
或super::方法名
- 引用构造方法:
类名::new
- 类名引用成员方法:
类名::实例方法名
- 引用数组的构造方法:
数组类型[]::new