方法引用::: 双冒号的四种用法(AI 辅助学习 Java 8)
上周学了 Lambda 表达式,今天学习它的更简洁写法——方法引用。用
::双冒号,一行 Lambda 可以缩成三个单词。
一、为什么需要方法引用?
Lambda 已经够简洁了?
// Lambda
list.stream().map(s -> s.length()).collect(toList());
// 方法引用
list.stream().map(String::length).collect(toList());
方法引用的本质:当 Lambda 体只是调用一个已有方法时,可以直接引用该方法。
二、四种方法引用类型
| 类型 | 语法 | Lambda 等价 | 示例 |
|---|---|---|---|
| 对象::实例方法 | obj::method | () -> obj.method() | System.out::println |
| 类::静态方法 | Class::staticMethod | (args) -> Class.staticMethod(args) | Integer::parseInt |
| 类::实例方法 | Class::instanceMethod | (obj, args) -> obj.instanceMethod(args) | String::length |
| 类::new | Class::new | (args) -> new Class(args) | StringBuilder::new |
三、对象 :: 实例方法
已有对象的实例方法可以直接引用:
// Lambda
Supplier<String> lambda = () -> System.currentTimeMillis().toString();
// 方法引用
Supplier<String> methodRef = System::currentTimeMillis;
// 最常见的:System.out::println
Consumer<String> print = System.out::println;
print.accept("Hello!");
// StringBuilder::append
BiConsumer<StringBuilder, String> append = StringBuilder::append;
StringBuilder sb = new StringBuilder("Hello");
append.accept(sb, " World"); // "Hello World"
四、类 :: 静态方法
静态方法的方法引用最直观:
// Integer.parseInt
Function<String, Integer> parseInt = Integer::parseInt;
parseInt.apply("123"); // 123
// Math.max
BiFunction<Integer, Integer, Integer> max = Math::max;
max.apply(3, 7); // 7
// String.valueOf
Function<Integer, String> valueOf = String::valueOf;
valueOf.apply(456); // "456"
Stream 中常用:
List<String> numbers = Arrays.asList("1", "2", "3", "4", "5");
List<Integer> parsed = numbers.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
五、类 :: 实例方法(任意对象)
这是最容易混淆的一种。当 Lambda 的第一个参数是调用方法的对象时:
// Lambda: (s) -> s.length()
Function<String, Integer> length = String::length;
length.apply("Hello"); // 5
// Lambda: (s) -> s.toUpperCase()
Function<String, String> upper = String::toUpperCase;
upper.apply("hello"); // "HELLO"
Stream 中常用:
List<String> words = Arrays.asList("lambda", "stream", "optional");
// 获取每个字符串长度
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// 按长度排序
List<String> sorted = words.stream()
.sorted(Comparator.comparing(String::length))
.collect(Collectors.toList());
// [stream, lambda, optional] -> [6, 6, 8]
六、构造器引用
// Supplier: () -> new ArrayList<>()
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get();
// Function: (x) -> new StringBuilder(x)
Function<String, StringBuilder> sbCreator = StringBuilder::new;
StringBuilder sb = sbCreator.apply("Hello");
// Stream 中使用
List<String> words = Arrays.asList("Java", "Lambda", "Stream");
List<BlogPost> posts = words.stream()
.map(BlogPost::new) // 调用 BlogPost(String) 构造器
.collect(Collectors.toList());
七、运行结果
=== 对象::实例方法 ===
Lambda: 1781318674287
引用: 1781318674318
Consumer 输出: Hello Method Reference!
BiConsumer: Hello World
=== 类::静态方法 ===
parseInt: 123
max(3, 7): 7
valueOf(456): 456
解析列表: [1, 2, 3, 4, 5]
=== 类::实例方法(任意对象) ===
String::length: 5
String::toUpperCase: HELLO
词长列表: [6, 6, 8, 3]
按长度排序: [ref, lambda, stream, optional]
=== 类::new(构造器引用) ===
ArrayList::new: [item1]
StringBuilder::new: Hello
int[]::new: [0, 0, 0, 0, 0]
BlogPost::new: [BlogPost{title='Java'}, BlogPost{title='8'}, ...]
八、什么时候能用方法引用?
能用的场景:Lambda 体只做了一件事——调用一个方法。
// ✅ 可以:只做了一件事
s -> s.length() → String::length
s -> Integer.parseInt(s) → Integer::parseInt
() -> list.size() → list::size
// ❌ 不可以:做了多件事或需要参数运算
s -> s.trim().length() → 不能直接引用
(a, b) -> a + b → 不能直接引用
x -> Math.abs(x) + 1 → 不能直接引用
九、本节速查
| 你想写 | 可以改成 |
|---|---|
x -> System.out.println(x) | System.out::println |
s -> Integer.parseInt(s) | Integer::parseInt |
s -> s.length() | String::length |
s -> s.toUpperCase() | String::toUpperCase |
() -> new ArrayList<>() | ArrayList::new |
s -> new StringBuilder(s) | StringBuilder::new |
十、下篇预告
周四我们将学习数组引用、方法引用组合,以及在博客项目中用方法引用进一步简化代码。
系列文章导航: