在Java 8中,构造器引用是一种特殊的方法引用,用于引用构造函数。它允许您使用 “::” 操作符来引用一个构造函数,而不是调用它。构造器引用的语法如下:
ClassName::new
其中,ClassName 是要引用的类的名称,后面跟着 :: 操作符和关键字 new。这种语法可以用来创建一个对构造函数的引用,而不实际调用它,从而可以将构造函数作为方法引用传递给函数式接口。
下面将分别介绍如何引用类中仅仅拥有一个构造方法、多个构造方法和数组。
构造器引用同方法引用类似,不同的是在构造器引用中方法名是 new。例如,Button::new 表示 Button 类的构造器引用。
package com.hxstrive.jdk8.constructor_ref; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 构造器引用 * @author hxstrive.com */ public class ConstructorRef01 { public static void main(String[] args) { List<ConstructorRef01.User> userList = Stream.of("Tom", "Helen") .map(ConstructorRef01.User::new) .collect(Collectors.toList()); for(ConstructorRef01.User user : userList) { System.out.println(user); } } static class User { private String name; public User(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } } }
运行示例,输出如下:
User{name='Tom'} User{name='Helen'}
对于拥有多个构造器的类,选择使用哪个构造器取决于上下文。假设你有一个字符串列表和一个数字列表,分别通过这两个列表去构建 ConstructorRef02.User 对象,使用如下表达式:
package com.hxstrive.jdk8.constructor_ref; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 构造器引用,根据上下文自动选择合适的构造器 * @author hxstrive.com */ public class ConstructorRef02 { public static void main(String[] args) { List<ConstructorRef02.User> userList = Stream.of("Tom", "Helen") .map(ConstructorRef02.User::new) .collect(Collectors.toList()); for(ConstructorRef02.User user : userList) { System.out.println(user); } System.out.println("======================================"); userList = Stream.of(100, 200) .map(ConstructorRef02.User::new) .collect(Collectors.toList()); for(ConstructorRef02.User user : userList) { System.out.println(user); } } static class User { private int id; private String name; // 构造方法1 public User(int id, String name) { this.id = id; this.name = name; } // 构造方法2 public User(int id) { this.id = id; } // 构造方法3 public User(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } } }
运行示例,输出如下:
User{id=0, name='Tom'} User{id=0, name='Helen'} ====================================== User{id=100, name='null'} User{id=200, name='null'}
我们还可以使用数组类型来编写构造器引用。例如,String[]::new 是一个含有一个参数的构造器引用,这个参数就是数组的长度。它等同于 Lambda 表达式 x->new String[x]。例如:
package com.hxstrive.jdk8.constructor_ref; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 构造器引用,用于数组引用 * @author hxstrive.com */ public class ConstructorRef04 { public static void main(String[] args) { List<String[]> strArrayList = Stream.of(10, 20).map(String[]::new).collect(Collectors.toList()); for(String[] strArray : strArrayList) { // 输出数组长度 System.out.println(strArray.length); } } }
运行示例,输出如下:
10 20
数组构造器引用可以用来绕过 Java 中的一个限制。在 Java 中,无法构造一个泛型类型 T 的数组。表达式 new T[n] 是错误的,因为它会被擦除为 new Object[n]。这对于编写 API 的开发人员来说是一个问题。例如,假设我们希望构造一组 String,Stream 接口中有一个返回 Object 数组的 toArray 方法:
Object[] strArray = stream.toArray();
但是这并不能让我们满意。用户希望是一组 String 对象,而不是一组 Object 对象。Stream API 通过构造器引用解决了这个问题。它允许将 String[]::new 传递给 toArray 方法:
String[] strArray = stream.toArray(String[]::new);
toArray 方法会调用该构造器来获得一个正确类型的数组,然后它会填充并返回该数组。例如:
package com.hxstrive.jdk8.constructor_ref; import java.util.stream.Stream; /** * 构造器引用,用于数组引用 * @author hxstrive.com */ public class ConstructorRef05 { public static void main(String[] args) { String[] strArray = Stream.of("Tom", "Helen").toArray(String[]::new); for(String str : strArray) { System.out.println(str); } } }
运行示例,输出如下:
Tom Helen