在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