调用getActualTypeArguments时丢失泛型类型参数的技术问题咨询
为什么会出现泛型参数丢失?
这本质上是Java泛型类型擦除机制和匿名内部类的泛型参数来源共同导致的:
- Java泛型的擦除特性:Java泛型是编译时特性,编译完成后大部分泛型信息会被擦除,只有当泛型参数是**具体的类(比如
AggregateRepository<Category>)**而非类型变量(比如AggregateRepository<T>)时,运行时才会保留泛型参数的具体类型信息。 - 匿名内部类的泛型依赖类型变量:你的
RepositoryFactory.create()方法中,返回的是一个继承自AggregateRepository<T>的匿名内部类,这里的T是方法的泛型类型变量。由于这个T在运行时没有被绑定到具体的类(编译时只是占位符),所以JVM无法在运行时为这个匿名类保留具体的泛型参数信息。当你通过clazz.getGenericSuperclass()获取父类类型时,拿到的ActualTypeArguments[0]是一个TypeVariable而非具体的Class实例,强转自然会出问题。
解决办法
这里提供两种可靠的解决方案,你可以根据项目结构选择:
方案一:显式传递泛型类的Class实例(最可靠)
核心思路是在创建Repository时,直接把具体的Class传递进去,让AggregateRepository持有这个类型,后续需要时直接获取,避免反射依赖。
- 修改
RepositoryFactory的create方法,接收泛型类的Class参数:
public class RepositoryFactory { private final EventStore eventstore; private final EventPublisher eventPublisher; public RepositoryFactory(EventStore eventstore, EventPublisher eventPublisher) { this.eventstore = eventstore; this.eventPublisher = eventPublisher; } // 新增Class<T>参数,明确传递具体类型 public <T extends AggregateRoot> Repository<T> create(Class<T> aggregateClass) { return new AggregateRepository<T>(eventstore, eventPublisher, aggregateClass) { @Override public void save(T aggregate, int version) { super.save(aggregate, version); } }; } }
- 修改
AggregateRepository,添加Class<T>成员变量并提供获取方法:
public class AggregateRepository<T extends AggregateRoot> implements Repository<T> { private final Class<T> aggregateClass; private final EventStore eventStore; private final EventPublisher eventPublisher; // 构造方法新增Class参数 public AggregateRepository(EventStore eventStore, EventPublisher eventPublisher, Class<T> aggregateClass) { this.eventStore = eventStore; this.eventPublisher = eventPublisher; this.aggregateClass = aggregateClass; } // 提供方法直接获取泛型类型 public Class<T> getAggregateClass() { return aggregateClass; } // 原有的save等方法... }
- 修改
BaseCommandHandler的构造方法,传递具体的Class:
public abstract class BaseCommandHandler<T extends AggregateRoot> { protected final Repository<T> repository; protected BaseCommandHandler(RepositoryFactory repositoryfactory, Class<T> aggregateClass) { this.repository = repositoryfactory.create(aggregateClass); } }
- 在
RemoveCategoryCommandHandler中调用父类构造时传入Category.class:
public class RemoveCategoryCommandHandler extends BaseCommandHandler<Category> implements CommandHandler<RemoveCategory> { public RemoveCategoryCommandHandler(RepositoryFactory repositoryfactory) { super(repositoryfactory, Category.class); } }
之后需要泛型类型时,直接调用((AggregateRepository<T>)repository).getAggregateClass()即可,完全避免反射问题。
方案二:通过子类的泛型参数反射获取(适合结构简单的场景)
如果你的BaseCommandHandler的子类都是直接指定具体泛型类型(比如RemoveCategoryCommandHandler直接继承BaseCommandHandler<Category>),可以通过反射获取子类继承父类时的泛型参数:
修改BaseCommandHandler的构造方法:
public abstract class BaseCommandHandler<T extends AggregateRoot> { protected final Repository<T> repository; protected final Class<T> aggregateClass; protected BaseCommandHandler(RepositoryFactory repositoryfactory) { // 获取当前子类继承父类时的泛型参数 ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 这里的ActualTypeArguments[0]就是具体的Category.class this.aggregateClass = (Class<T>) pt.getActualTypeArguments()[0]; this.repository = repositoryfactory.create(); } }
这样在RemoveCategoryCommandHandler中无需额外传参,直接调用父类构造即可,后续需要类型时直接使用aggregateClass成员变量。
注意:如果你的子类是泛型类(比如有一层中间泛型类
GenericCommandHandler<T>继承BaseCommandHandler<T>,再由RemoveCategoryCommandHandler继承GenericCommandHandler<Category>),这种反射方式就会失效,因为此时getGenericSuperclass()拿到的泛型参数是类型变量T而非具体类,这种情况下方案一更稳妥。
内容的提问来源于stack exchange,提问作者Sudarshan

