方法引用与函数式接口

什么是方法引用

一句话理解:方法引用是 Lambda表达式进一步简写,当Lambda里只是调用一个已有的方法时,直接用 :: 指向那个方法就行了

打个比方:Lambda是”你帮我做这件事”,方法引用是”你知道怎么做,我就不啰嗦了”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testMethodRefBasic() {
System.out.println("=== 方法引用基础 ===");

List<String> names = Arrays.asList("Charlie", "Alice", "Bob");

// Lambda写法
names.forEach(name -> System.out.println(name));

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

// 两者完全等价,方法引用只是更简洁的语法糖
}

四种方法引用

1. 静态方法引用:类名::静态方法名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void testStaticMethodRef() {
System.out.println("=== 静态方法引用 ===");

// Lambda写法
Function<String, Integer> parser1 = s -> Integer.parseInt(s);

// 方法引用写法
Function<String, Integer> parser2 = Integer::parseInt;

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

// 再比如 Math.abs
Function<Integer, Integer> abs = Math::abs;
System.out.println(abs.apply(-5)); // 5
}

2. 实例方法引用:对象::实例方法名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testInstanceMethodRef() {
System.out.println("=== 实例方法引用 ===");

String str = "Hello World";

// Lambda写法
Supplier<Integer> len1 = () -> str.length();

// 方法引用写法:具体对象的方法
Supplier<Integer> len2 = str::length;

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

// 常见场景:System.out 是一个对象
// System.out::println 就是 System.out 这个对象的 println 方法
Consumer<String> printer = System.out::println;
printer.accept("打印这句话");
}

3. 特定类型任意对象的实例方法引用:类名::实例方法名

这个最容易搞混,关键是第一个参数当调用者

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
@Test
public void testArbitraryObjectMethodRef() {
System.out.println("=== 特定类型任意对象方法引用 ===");

// Lambda写法
Function<String, String> lower1 = s -> s.toLowerCase();

// 方法引用写法:String类的任意对象调用toLowerCase
Function<String, String> lower2 = String::toLowerCase;

System.out.println(lower2.apply("HELLO")); // hello

// 理解要点:
// String::toLowerCase 等价于 (String s) -> s.toLowerCase()
// 第一个参数s就是调用者

// 两个参数的情况
BiFunction<String, String, Boolean> contains = String::contains;
// 等价于 (s1, s2) -> s1.contains(s2),s1是调用者
System.out.println(contains.apply("Hello World", "World")); // true

// 排序场景
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
names.sort(String::compareToIgnoreCase);
System.out.println(names); // [Alice, Bob, Charlie]
}

4. 构造器引用:类名::new

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
@Test
public void testConstructorRef() {
System.out.println("=== 构造器引用 ===");

// Lambda写法
Supplier<ArrayList<String>> listFactory1 = () -> new ArrayList<>();

// 构造器引用写法
Supplier<ArrayList<String>> listFactory2 = ArrayList::new;

List<String> list = listFactory2.get();
list.add("Hello");
System.out.println(list); // [Hello]

// 带参数的构造器引用
Function<String, StringBuilder> sbFactory = StringBuilder::new;
StringBuilder sb = sbFactory.apply("初始值");
System.out.println(sb); // 初始值

// 实战:Stream里把字符串转成对象
List<String> nameStrs = Arrays.asList("张三", "李四", "王五");
List<Student> students = nameStrs.stream()
.map(Student::new) // 调用 Student(String name) 构造方法
.collect(Collectors.toList());
}

四种方法引用对比

类型 语法 等价Lambda 示例
静态方法引用 类名::静态方法 x -> 类名.方法(x) Integer::parseInt
实例方法引用 对象::方法 () -> 对象.方法() str::length
任意对象方法引用 类名::实例方法 x -> x.方法() String::toLowerCase
构造器引用 类名::new x -> new 类名(x) ArrayList::new

四大核心函数式接口

Java在 java.util.function 包里内置了很多函数式接口,最核心的就这四个

1. Predicate —— 判断型(T -> boolean)

接收一个参数,返回true/false,常用于过滤

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
@Test
public void testPredicate() {
System.out.println("=== Predicate ===");

Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> isPositive = n -> n > 0;

System.out.println(isEven.test(4)); // true
System.out.println(isEven.test(3)); // false

// 组合判断
Predicate<Integer> isEvenAndPositive = isEven.and(isPositive);
Predicate<Integer> isEvenOrPositive = isEven.or(isPositive);
Predicate<Integer> isOdd = isEven.negate(); // 取反

System.out.println(isEvenAndPositive.test(4)); // true
System.out.println(isEvenAndPositive.test(-4)); // false
System.out.println(isOdd.test(3)); // true

// 在Stream里用
List<Integer> numbers = Arrays.asList(-3, -1, 0, 1, 2, 3, 4);
List<Integer> result = numbers.stream()
.filter(isEven.and(isPositive))
.collect(Collectors.toList());
System.out.println("偶数且正数:" + result); // [2, 4]
}

2. Function<T, R> —— 转换型(T -> R)

接收一个参数,返回一个结果,常用于 [Stream API](/2026/04/04/Stream API/) 的map操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testFunction() {
System.out.println("=== Function ===");

Function<String, Integer> strToLen = String::length;
Function<Integer, String> intToStr = n -> "数字是:" + n;

System.out.println(strToLen.apply("Hello")); // 5

// andThen:先执行自己,再执行参数里的Function
Function<String, String> lenThenStr = strToLen.andThen(intToStr);
System.out.println(lenThenStr.apply("Hello")); // 数字是:5

// compose:先执行参数里的Function,再执行自己(跟andThen反过来)
Function<Integer, Integer> doubleIt = n -> n * 2;
Function<String, Integer> lenThenDouble = doubleIt.compose(strToLen);
System.out.println(lenThenDouble.apply("Hello")); // 10
}

3. Consumer —— 消费型(T -> void)

接收一个参数,不返回结果,常用于遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testConsumer() {
System.out.println("=== Consumer ===");

Consumer<String> print = System.out::println;
Consumer<String> shout = s -> System.out.println(s.toUpperCase() + "!!!");

print.accept("hello"); // hello
shout.accept("hello"); // HELLO!!!

// andThen:链式消费
Consumer<String> printThenShout = print.andThen(shout);
printThenShout.accept("hi");
// hi
// HI!!!

// 在forEach里
List<String> names = Arrays.asList("Alice", "Bob");
names.forEach(print);
}

4. Supplier —— 供给型(() -> T)

不接收参数,返回一个结果,常用于懒加载和工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testSupplier() {
System.out.println("=== Supplier ===");

Supplier<String> helloSupplier = () -> "Hello World";
Supplier<List<String>> listFactory = ArrayList::new;
Supplier<Double> randomSupplier = Math::random;

System.out.println(helloSupplier.get()); // Hello World
System.out.println(listFactory.get()); // []
System.out.println(randomSupplier.get()); // 0.xxxx

// 实战:配合Optional的orElseGet使用
Optional<String> empty = Optional.empty();
String result = empty.orElseGet(() -> "默认值"); // Supplier提供默认值
System.out.println(result); // 默认值
}

更多Optional配合函数式接口的用法见 Optional类

四大接口对比表

接口 方法 参数 返回值 用途 常见场景
Predicate<T> test(T) T boolean 判断 filter过滤
Function<T,R> apply(T) T R 转换 map转换
Consumer<T> accept(T) T void 消费 forEach遍历
Supplier<T> get() T 供给 orElseGet默认值

扩展接口(了解即可)

扩展接口 说明
BiPredicate<T,U> 两个参数的判断
BiFunction<T,U,R> 两个参数的转换
BiConsumer<T,U> 两个参数的消费
UnaryOperator<T> Function<T,T> 的特例,输入输出同类型
BinaryOperator<T> BiFunction<T,T,T> 的特例
IntPredicate / LongPredicate 基本类型特化,避免装箱

常见坑

坑1:搞混 String::toLowerCasestr::toLowerCase

String::toLowerCase → 任意对象方法引用,需要一个String参数当调用者

str::toLowerCase → 特定对象方法引用,str已经确定了,不需要参数

1
2
3
4
5
6
7
String str = "HELLO";

// Function<String, String>,接收一个String参数
Function<String, String> f1 = String::toLowerCase;

// Supplier<String>,不需要参数(str已经绑定了)
Supplier<String> f2 = str::toLowerCase;

坑2:方法引用的方法签名必须匹配函数式接口

参数类型和返回类型要对得上,否则编译报错

坑3:重载方法可能导致歧义

如果一个类有多个同名重载方法,方法引用可能无法确定用哪个,需要手动指定

练习题

题1:用方法引用重写以下Lambda

1
2
3
4
5
// 重写这些Lambda为方法引用
Function<String, Integer> f1 = s -> Integer.valueOf(s);
Consumer<Object> f2 = o -> System.out.println(o);
Supplier<Thread> f3 = () -> new Thread();
Function<String, String> f4 = s -> s.trim();

答案:Integer::valueOfSystem.out::printlnThread::newString::trim

题2:用Predicate组合实现:判断一个字符串不为null、不为空、长度大于5

题3:用Function的andThen链式调用实现:字符串 → 去空格 → 转大写 → 取长度

题4:写一个通用的过滤方法 <T> List<T> filterList(List<T> list, Predicate<T> predicate),用Predicate参数来决定过滤条件


上一章 目录 下一章
Optional类 java基础 日期时间API