教程中心 / Java教程 / 方法引用

方法引用

15分钟 函数式编程

引言:什么是方法引用?

方法引用(Method Reference)是Java 8引入的重要特性,是Lambda表达式的一种简化形式。当Lambda表达式只是直接调用某个方法时,可以使用方法引用来更简洁地表达。

方法引用类比

想象你在指挥乐队:

Lambda表达式写法:
"请这位乐手(小明)演奏他的小提琴"

方法引用写法:
"小明::演奏小提琴" 或直接 "小明::演奏"

这就是方法引用:直接指向某个方法,而不是描述整个动作

为什么使用方法引用?

更简洁:代码更短,更易读
更清晰:直接指向已有方法,而不是描述动作
更高效:编译器可以直接优化

Lambda vs 方法引用对比

// Lambda表达式
list.forEach(s -> System.out.println(s));

// 方法引用(更简洁)
list.forEach(System.out::println);

System.out::println 指向 System.out.println(String) 方法

四种方法引用类型

类型语法说明示例
静态方法引用ClassName::staticMethod引用静态方法Integer::parseInt
实例方法引用(特定对象)instance::instanceMethod引用某对象的实例方法str::length
实例方法引用(任意对象)ClassName::instanceMethod引用某类型任意对象的实例方法String::toUpperCase
构造方法引用ClassName::new引用构造方法ArrayList::new

类型1:静态方法引用

静态方法引用指向静态方法,语法为 ClassName::staticMethod

语法规则

// Lambda表达式
(input) -> ClassName.staticMethod(input)

// 方法引用
ClassName::staticMethod

示例

// Lambda表达式
Function parser1 = s -> Integer.parseInt(s);

// 静态方法引用
Function parser2 = Integer::parseInt;

// 使用
System.out.println(parser2.apply("123"));  // 123

更多静态方法引用示例

// Math类的静态方法
Predicate isEven1 = n -> Math.abs(n) % 2 == 0;
Predicate isEven2 = Math::isNaN;  // 注意:这个不对,应该用abs

// 正确示例
UnaryOperator abs = Math::abs;
System.out.println(abs.apply(-10));  // 10

// String类的静态方法
Function length1 = s -> s.length();
Function length2 = String::length;  // length不是静态的

// 正确的静态方法引用
Function upper1 = s -> s.toUpperCase();
Function upper2 = String::toUpperCase;  // 错误!toUpperCase不是静态的

类型2:实例方法引用(特定对象)

实例方法引用指向某个具体对象的实例方法,语法为 instance::instanceMethod

语法规则

// Lambda表达式
() -> instance.instanceMethod()

// 方法引用
instance::instanceMethod

示例

String str = "Hello World";

// Lambda表达式
Supplier len1 = () -> str.length();

// 实例方法引用
Supplier len2 = str::length;

// 使用
System.out.println(len2.get());  // 11

更多示例

// 使用System.out
Consumer printer1 = s -> System.out.println(s);
Consumer printer2 = System.out::println;
printer2.accept("Hello");  // 打印 "Hello"

// 使用某个具体对象的方法
List list = Arrays.asList("a", "b", "c");
list.forEach(s -> System.out.println(s));      // Lambda
list.forEach(System.out::println);             // 方法引用

类型3:实例方法引用(任意对象)

特定类型的方法引用指向某类型任意对象的实例方法,语法为 ClassName::instanceMethod。也叫绑定方法引用

关键理解:当Lambda表达式的第一个参数是方法的调用者,后面参数是方法参数时,可以使用这种形式。

语法规则

// Lambda表达式:(obj, param) -> obj.instanceMethod(param)

// 方法引用:ClassName::instanceMethod

// 例如:String::toUpperCase 相当于 (String s) -> s.toUpperCase()

示例

// Lambda表达式
Function upper1 = s -> s.toUpperCase();

// 特定类型的方法引用
Function upper2 = String::toUpperCase;

// 使用
System.out.println(upper2.apply("hello"));  // HELLO

两参数的方法引用

// Lambda表达式
BiPredicate contains1 = (s1, s2) -> s1.contains(s2);

// 特定类型的方法引用
BiPredicate contains2 = String::contains;

// 使用
System.out.println(contains2.test("Hello", "ell"));  // true

更多示例

List names = Arrays.asList("alice", "bob", "charlie");

// Lambda
names.stream().map(s -> s.toUpperCase()).forEach(System.out::println);

// 方法引用
names.stream().map(String::toUpperCase).forEach(System.out::println);

// 比较器
Comparator comp1 = (s1, s2) -> s1.compareTo(s2);
Comparator comp2 = String::compareTo;

类型4:构造方法引用

构造方法引用指向构造方法,语法为 ClassName::new

语法规则

// Lambda表达式
() -> new ClassName()
(args) -> new ClassName(args)

// 构造方法引用
ClassName::new

无参构造方法引用

// Lambda表达式
Supplier> list1 = () -> new ArrayList<>();

// 构造方法引用
Supplier> list2 = ArrayList::new;

// 使用
ArrayList arr = list2.get();

有参构造方法引用

// Lambda表达式
Function> list1 = size -> new ArrayList<>(size);

// 构造方法引用
Function> list2 = ArrayList::new;

// 使用
ArrayList arr = list2.apply(10);  // 创建初始容量为10的列表

自定义类的构造方法引用

// Lambda表达式
Function person1 = name -> new Person(name);

// 构造方法引用
Function person2 = Person::new;

// 使用
Person p = person2.apply("张三");
System.out.println(p.getName());  // 张三

方法引用在Stream中的应用

map方法

List names = Arrays.asList("alice", "bob", "charlie");

// Lambda
List upper1 = names.stream()
    .map(s -> s.toUpperCase())
    .collect(Collectors.toList());

// 方法引用
List upper2 = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

filter方法

List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

// Lambda
List evens1 = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());

// 自定义方法引用
List evens2 = numbers.stream()
    .filter(NumberUtils::isEven)  // 假设有这个静态方法
    .collect(Collectors.toList());

forEach方法

List list = Arrays.asList("Java", "Python", "C++");

// Lambda
list.forEach(s -> System.out.println(s));

// 方法引用
list.forEach(System.out::println);

compare方法

List languages = Arrays.asList("Java", "Python", "C++");

// Lambda
languages.sort((s1, s2) -> s1.compareTo(s2));

// 方法引用
languages.sort(String::compareTo);

// 倒序
languages.sort(Comparator.reverseOrder());

方法引用的限制

不是所有Lambda都能变成方法引用

不能使用方法引用的情况

// 情况1:Lambda表达式包含复杂逻辑
list.stream().map(s -> {
    String result = s.trim().toUpperCase();
    return result;
});
// 不能简化为方法引用,因为逻辑太复杂

// 情况2:需要类型转换
list.stream().map(s -> (String)s.toLowerCase());
// 不能简化为方法引用

// 情况3:方法有多个参数且不只是简单的调用
BiFunction concat = (s1, s2) -> s1.concat(s2);
BiFunction concat2 = String::concat;
// 这个可以!因为concat就是简单的调用

如何选择:Lambda还是方法引用?

场景推荐原因
Lambda只是调用方法方法引用更简洁、更清晰
Lambda包含复杂逻辑Lambda方法引用无法表达
Lambda需要计算多个值Lambda需要多步操作
只是打印或传递方法引用如System.out::println

最佳实践

// 推荐:方法引用
list.forEach(System.out::println);
map(String::toUpperCase);
set.stream().map(Object::toString);

// 推荐:Lambda(复杂逻辑)
list.stream().map(s -> s.trim().toLowerCase().replace(" ", ""));
list.stream().filter(s -> {
    // 复杂逻辑
    return s.length() > 5 && s.startsWith("a");
});

小结

  • 方法引用是Lambda的简化形式,使代码更简洁
  • 静态方法引用ClassName::staticMethod
  • 实例方法引用(特定对象)instance::instanceMethod
  • 实例方法引用(任意对象)ClassName::instanceMethod
  • 构造方法引用ClassName::new
  • 当Lambda只是调用方法时,优先使用方法引用
  • 方法引用不能替代所有Lambda,复杂逻辑仍用Lambda
☕ Java 在线代码编辑器
📝 运行结果