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仓库


原始标题:Create an Instance of Generic Type in Java | Baeldung