方法引用::: 双冒号的四种用法(AI 辅助学习 Java 8)

15 阅读3分钟

方法引用::: 双冒号的四种用法(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
类::newClass::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

十、下篇预告

周四我们将学习数组引用、方法引用组合,以及在博客项目中用方法引用进一步简化代码。


系列文章导航