双冒号(::)是Java 8引入的方法引用语法,用于将已有方法绑定到函数接口,如System.out::println绑定到Consumer<String>;需满足类型兼容,分对象::实例方法、类::静态方法、类::实例方法三类。

双冒号(::)是 Java 8 引入的方法引用语法,它不是“调用”方法,而是把一个**已有方法的签名与函数接口抽象方法对齐后,作为其实现直接绑定过去**。`System.out::println` 是最典型例子——它不执行打印,而是生成一个符合 Consumer<T> 接口的实例。
核心前提:函数接口抽象方法签名必须匹配目标方法
方法引用能成立,关键在于“类型兼容”。以 System.out::println 为例:
-
System.out是PrintStream实例,其println(String)、println(int)等重载方法,都属于实例方法 -
Consumer<String>的抽象方法是void accept(T t),形参类型为T,返回 void - 当写
Consumer<String> c = System.out::println;,编译器会自动选取println(String)这个重载版本,因为参数类型和返回值完全匹配
三种方法引用形式,System.out::println 属于其中一种
根据被引用方法的归属,方法引用分三类:
-
对象::实例方法 —— 如
System.out::println、list::add。左侧是具体对象,右侧是它的一个实例方法,参数列表自动补上前置接收者(即this) -
类::静态方法 —— 如
Objects::isNull、String::valueOf。等价于(arg) -> 类.静态方法(arg) -
类::实例方法 —— 如
String::length、String::compareTo。此时第一个参数被视为调用对象,后续参数才是原方法的实参,等价于(str) -> str.length()或(s1, s2) -> s1.compareTo(s2)
实际怎么用?结合常见函数接口场景
不是所有地方都能直接写 ::,它必须出现在**需要函数接口类型的位置**,比如 lambda 参数、赋值、方法传参:
- 集合遍历:
list.forEach(System.out::println);(forEach参数是Consumer<? super E>) - 流处理:
stream.map(String::toUpperCase).forEach(System.out::println); - 创建比较器:
Comparator<Person> byAge = Comparator.comparing(Person::getAge); - 自定义函数接口接收:
MyProcessor processor = System.out::println;(前提是MyProcessor定义了单抽象方法,且签名与println兼容)
注意边界:什么情况下会失败?
看似简单,但几个典型坑容易忽略:
-
重载歧义:如果
System.out有多个println且编译器无法根据上下文推断(比如目标接口是Function<Object, Void>),就会报错 -
泛型擦除干扰:如
List<Integer> nums = ...; nums.forEach(System.out::println);实际调用的是println(int),但如果误写成nums.stream().map(Object::toString).forEach(System.out::println),这里仍是println(String),没问题;但若中间类型模糊,可能触发编译错误 -
不能用于需要多参数却只提供单参数引用的场景:例如
BiConsumer<String, Integer> b = System.out::println;❌ 不合法——println没有两个参数的重载版本匹配accept(String, Integer)

















