什么是方法引用
一句话理解:方法引用是 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");
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("=== 静态方法引用 ===");
Function<String, Integer> parser1 = s -> Integer.parseInt(s);
Function<String, Integer> parser2 = Integer::parseInt;
System.out.println(parser2.apply("123"));
Function<Integer, Integer> abs = Math::abs; System.out.println(abs.apply(-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";
Supplier<Integer> len1 = () -> str.length();
Supplier<Integer> len2 = str::length;
System.out.println(len2.get());
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("=== 特定类型任意对象方法引用 ===");
Function<String, String> lower1 = s -> s.toLowerCase();
Function<String, String> lower2 = String::toLowerCase;
System.out.println(lower2.apply("HELLO"));
BiFunction<String, String, Boolean> contains = String::contains;
System.out.println(contains.apply("Hello World", "World"));
List<String> names = Arrays.asList("Charlie", "Alice", "Bob"); names.sort(String::compareToIgnoreCase); System.out.println(names); }
|
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("=== 构造器引用 ===");
Supplier<ArrayList<String>> listFactory1 = () -> new ArrayList<>();
Supplier<ArrayList<String>> listFactory2 = ArrayList::new;
List<String> list = listFactory2.get(); list.add("Hello"); System.out.println(list);
Function<String, StringBuilder> sbFactory = StringBuilder::new; StringBuilder sb = sbFactory.apply("初始值"); System.out.println(sb);
List<String> nameStrs = Arrays.asList("张三", "李四", "王五"); List<Student> students = nameStrs.stream() .map(Student::new) .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)); System.out.println(isEven.test(3));
Predicate<Integer> isEvenAndPositive = isEven.and(isPositive); Predicate<Integer> isEvenOrPositive = isEven.or(isPositive); Predicate<Integer> isOdd = isEven.negate();
System.out.println(isEvenAndPositive.test(4)); System.out.println(isEvenAndPositive.test(-4)); System.out.println(isOdd.test(3));
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. 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"));
Function<String, String> lenThenStr = strToLen.andThen(intToStr); System.out.println(lenThenStr.apply("Hello"));
Function<Integer, Integer> doubleIt = n -> n * 2; Function<String, Integer> lenThenDouble = doubleIt.compose(strToLen); System.out.println(lenThenDouble.apply("Hello")); }
|
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"); shout.accept("hello");
Consumer<String> printThenShout = print.andThen(shout); printThenShout.accept("hi");
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()); System.out.println(listFactory.get()); System.out.println(randomSupplier.get());
Optional<String> empty = Optional.empty(); String result = empty.orElseGet(() -> "默认值"); 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::toLowerCase 和 str::toLowerCase
String::toLowerCase → 任意对象方法引用,需要一个String参数当调用者
str::toLowerCase → 特定对象方法引用,str已经确定了,不需要参数
1 2 3 4 5 6 7
| String str = "HELLO";
Function<String, String> f1 = String::toLowerCase;
Supplier<String> f2 = str::toLowerCase;
|
坑2:方法引用的方法签名必须匹配函数式接口
参数类型和返回类型要对得上,否则编译报错
坑3:重载方法可能导致歧义
如果一个类有多个同名重载方法,方法引用可能无法确定用哪个,需要手动指定
练习题
题1:用方法引用重写以下Lambda
1 2 3 4 5
| 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::valueOf、System.out::println、Thread::new、String::trim
题2:用Predicate组合实现:判断一个字符串不为null、不为空、长度大于5
题3:用Function的andThen链式调用实现:字符串 → 去空格 → 转大写 → 取长度
题4:写一个通用的过滤方法 <T> List<T> filterList(List<T> list, Predicate<T> predicate),用Predicate参数来决定过滤条件