Java方法引用与构造方法引用的语法

Java方法引用是Lambda表达式的简写,有四种形式:对象::实例方法、类名::静态方法、类名::实例方法、类名::new;需严格匹配函数式接口签名,构造引用依赖参数类型选择重载构造器,数组引用等价IntFunction,且不隐藏受检异常。

方法引用的基本语法和常见形式

Java 方法引用本质是 Lambda 表达式的简写,前提是已有现成方法满足函数式接口的签名。它不是新语法糖,而是编译器在类型推导后生成的等价字节码。

四种合法形式对应不同调用场景,必须严格匹配目标接口的抽象方法参数与返回类型:

  • 对象::实例方法名:如 str::toLowerCase,要求接口方法参数列表为 (T, ...),第一个参数是该对象类型,后续参数传给实例方法
  • 类名::静态方法名:如 Objects::isNull,接口方法参数列表需与静态方法一致
  • 类名::实例方法名:如 String::length,这是最易混淆的一种——接口方法**必须有且仅有一个参数**,该参数作为调用实例,方法体在该参数上调用指定实例方法
  • 类名::new:构造方法引用,见下文

构造方法引用的写法与限制

构造方法引用用 类名::new 表示,它会被绑定到函数式接口中抽象方法的参数上,参数顺序和类型必须与某一个构造方法完全一致。

例如:Supplier> sup = ArrayList::new 合法,因为 ArrayList() 无参;而 Functio

n> f = ArrayList::new 也合法,对应 ArrayList(int) 构造器。

常见错误包括:

  • 目标接口方法返回类型不是该类或其子类 → 编译失败
  • 传入参数个数或类型无法匹配任一构造方法 → 报错 no suitable constructor found
  • 试图引用私有构造方法(且无访问权限)→ 编译不通过
  • 泛型类型擦除导致构造器签名冲突(如 Pair::newPair::new 在运行时无法区分)→ 需显式类型提示

为什么 String::new 有时能匹配多个构造器?

编译器根据函数式接口抽象方法的参数数量和类型,从重载的构造方法中选择唯一可匹配项。比如:

Function f1 = String::new;           // 匹配 String(String)
BiFunction f2 = String::new; // 匹配 String(String, Integer)

但如果存在两个构造器参数类型兼容(如 String(char[])String(String)),而接口方法只传一个 Object,编译器就无法确定 → 直接报错。

此时必须拆成 Lambda 显式调用,或添加类型转换:

Function f = String::new; // 明确指向 char[] 构造器

容易被忽略的细节:数组构造引用和异常传播

数组构造可写作 int[]::newString[]::new 等,它等价于接收一个 int 参数并返回对应长度数组的函数,即 IntFunction 类型。

方法引用不会隐藏受检异常。若原方法声明抛出 IOException,而函数式接口抽象方法没声明该异常,则不能直接引用,必须包装或改用自定义函数接口。

另外,方法引用在运行时是惰性求值的,但对象引用本身在创建时就被捕获——如果写 obj::methodobj 是 null,引用创建不报错,但首次调用时才抛 NullPointerException