1. 概述
泛型(Generics)为代码库提供了优雅的抽象层,既提升了代码复用性,又增强了代码质量。但在实际开发中,当我们尝试创建泛型类型的实例时,会遇到一些挑战——这源于Java泛型的设计机制。
本文将首先分析为什么直接实例化泛型类型会失败,然后探讨几种可行的解决方案。
2. 理解类型擦除
在深入之前,必须明确:泛型在编译时和运行时表现不同。
由于类型擦除(Type Erasure)机制的存在,泛型类型在运行时会被丢弃。简单说:
- 编译时:要求提供泛型类型信息
- 运行时:完全丢弃这些信息
编译器会从泛型类和方法中移除所有类型参数和类型参数信息。这种设计使Java应用能兼容泛型诞生前的旧版库。
正因如此,我们不能直接用new
关键字创建泛型实例:
public class GenericClass<T> {
private T t;
public GenericClass() {
this.t = new T(); // 编译失败
}
}
由于泛型是编译时概念,运行时类型信息已擦除,JVM无法为new T()
生成字节码——因为T
是未知类型。实际运行时,泛型类型会被替换为:
- 第一个边界类型(如果定义了边界)
Object
类(未定义边界时)
3. 示例准备
我们用一个消息发送服务贯穿全文。首先定义Sender
接口:
public interface Sender {
String send();
}
创建邮件发送实现类:
public class EmailSender implements Sender {
private String message;
@Override
public String send() {
return "EMAIL";
}
}
再创建一个泛型通知发送器:
public class NotificationSender<T> implements Sender {
private T body;
@Override
public String send() {
return "NOTIFICATION";
}
}
现在开始探索泛型实例化的多种方法。
4. 使用反射
反射是最常见的解决方案。核心前提:必须知道要创建对象的实际类型。
定义反射服务类:
public class SenderServiceReflection<T extends Sender> {
private Class<T> clazz;
public SenderServiceReflection(Class<T> clazz) {
this.clazz = clazz;
}
}
通过Class<T>
存储目标类型信息。添加实例创建方法:
public T createInstance() {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Error while creating an instance.");
}
}
✅ 关键点:
- 使用
getDeclaredConstructor().newInstance()
- 目标类必须有无参构造器
- 注意:Java 9后直接调用
Class.newInstance()
已废弃
测试用例:
@Test
void givenEmailSender_whenCreateInstanceUsingReflection_thenReturnResult() {
SenderServiceReflection<EmailSender> service = new SenderServiceReflection<>(EmailSender.class);
Sender emailSender = service.createInstance();
String result = emailSender.send();
assertEquals("EMAIL", result);
}
⚠️ 反射的局限性:
- 无法处理嵌套泛型(如
NotificationSender<String>
) - 尝试以下代码会直接编译失败:
SenderServiceReflection<NotificationSender<String>> service =
new SenderServiceReflection<>(NotificationSender<String>.class); // 编译错误
错误信息:Cannot select from parameterized type
5. 使用Supplier接口
Java 8的Supplier
函数式接口提供了更优雅的解决方案:
public class SenderServiceSupplier<T extends Sender> {
private Supplier<T> supplier;
public SenderServiceSupplier(Supplier<T> supplier) {
this.supplier = supplier;
}
public T createInstance() {
return supplier.get();
}
}
通过构造器传入Supplier<T>
,调用get()
获取实例。
基础用法(方法引用)
@Test
void givenEmailSender_whenCreateInstanceUsingSupplier_thenReturnResult() {
SenderServiceSupplier<EmailSender> service = new SenderServiceSupplier<>(EmailSender::new);
Sender emailSender = service.createInstance();
String result = emailSender.send();
assertEquals("EMAIL", result);
}
带参构造器(Lambda表达式)
@Test
void givenEmailSenderWithCustomConstructor_whenCreateInstanceUsingSupplier_thenReturnResult() {
SenderServiceSupplier<EmailSender> service = new SenderServiceSupplier<>(() -> new EmailSender("Baeldung"));
Sender emailSender = service.createInstance();
String result = emailSender.send();
assertEquals("EMAIL", result);
}
处理嵌套泛型
✅ 完美支持嵌套泛型场景:
@Test
void givenNotificationSender_whenCreateInstanceUsingSupplier_thenReturnCorrectResult() {
SenderServiceSupplier<NotificationSender<String>> service = new SenderServiceSupplier<>(
NotificationSender::new);
Sender notificationSender = service.createInstance();
String result = notificationSender.send();
assertEquals("NOTIFICATION", result);
}
6. 使用工厂模式
工厂模式能实现类似效果,且更符合面向对象设计原则。
首先定义工厂接口:
public interface Factory<T> {
T create();
}
创建服务类:
public class SenderServiceFactory<T extends Sender> {
private final Factory<T> factory;
public SenderServiceFactory(Factory<T> factory) {
this.factory = factory;
}
public T createInstance() {
return factory.create();
}
}
基础测试
@Test
void givenEmailSender_whenCreateInstanceUsingFactory_thenReturnResult() {
SenderServiceFactory<EmailSender> service = new SenderServiceFactory<>(EmailSender::new);
Sender emailSender = service.createInstance();
String result = emailSender.send();
assertEquals("EMAIL", result);
}
嵌套泛型支持
@Test
void givenNotificationSender_whenCreateInstanceUsingFactory_thenReturnResult() {
SenderServiceFactory<NotificationSender<String>> service = new SenderServiceFactory<>(
() -> new NotificationSender<>("Hello from Baeldung"));
NotificationSender<String> notificationSender = service.createInstance();
String result = notificationSender.send();
assertEquals("NOTIFICATION", result);
assertEquals("Hello from Baeldung", notificationSender.getBody());
}
7. 使用Guava库
Guava的TypeToken
类通过反射在运行时保留泛型信息:
public class SenderServiceGuava<T extends Sender> {
TypeToken<T> typeToken;
public SenderServiceGuava(Class<T> clazz) {
this.typeToken = TypeToken.of(clazz);
}
public T createInstance() {
try {
return (T) typeToken.getRawType().getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
通过getRawType()
获取运行时类类型。
基础测试
@Test
void givenEmailSender_whenCreateInstanceUsingGuava_thenReturnResult() {
SenderServiceGuava<EmailSender> service = new SenderServiceGuava<>(EmailSender.class);
Sender emailSender = service.createInstance();
String result = emailSender.send();
assertEquals("EMAIL", result);
}
匿名类方案
定义匿名TypeToken
:
TypeToken<T> typeTokenAnonymous = new TypeToken<T>(getClass()) {
};
public T createInstanceAnonymous() {
try {
return (T) typeTokenAnonymous.getRawType().getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
测试匿名类实现:
@Test
void givenEmailSender_whenCreateInstanceUsingGuavaAndAnonymous_thenReturnResult() {
SenderServiceGuava<EmailSender> service = new SenderServiceGuava<EmailSender>() {
};
Sender emailSender = service.createInstanceAnonymous();
String result = emailSender.send();
assertEquals("EMAIL", result);
}
嵌套泛型支持
✅ 匿名类方案完美支持嵌套泛型:
@Test
void givenNotificationSender_whenCreateInstanceUsingGuavaAndAnonymous_thenReturnResult() {
SenderServiceGuava<NotificationSender<String>> service = new SenderServiceGuava<NotificationSender<String>>() {
};
Sender notificationSender = service.createInstanceAnonymous();
String result = notificationSender.send();
assertEquals("NOTIFICATION", result);
}
8. 总结
本文系统探讨了Java中创建泛型实例的四种方案:
方案 | 优点 | 缺点 |
---|---|---|
反射 | 简单直接 | ❌ 不支持嵌套泛型 ❌ 需无参构造器 |
Supplier | ✅ 支持嵌套泛型 ✅ 支持带参构造器 |
需Java 8+ |
工厂模式 | ✅ 设计优雅 ✅ 支持嵌套泛型 |
需额外定义工厂接口 |
Guava | ✅ 运行时保留泛型信息 ✅ 支持嵌套泛型 |
引入第三方依赖 |
推荐选择:
- 简单场景:反射(无嵌套泛型时)
- Java 8+项目:优先使用
Supplier
- 复杂泛型场景:Guava的
TypeToken
完整源码见GitHub仓库