Say, I have an abstract class for testing.
abstract class BaseEntity<SELF extends BaseEntity<SELF>> {
}
I declared an abstract test class for testing subclasses of the BaseEntity
.
abstract BaseEntityTest<ENTITY extends BaseEntity<ENTITY>> {
@ArgumentsSource(BaseEntityTestArgumentsProvider.class)
@ParameterizedTest
void _NotBlank_ToString(final Entity entityInstance) {
}
protected final Class<ENTITY> entityClass;
}
public class BaseEntityArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(final ParameterDeclarations parameters,
final ExtensionContext context)
throws Exception {
final var firstParameterType = parameters.getFirst().map(ParameterDeclaration::getParameterType).orElse(null);
if (firstParameterType == null) {
return Stream.empty();
}
if (!BaseEntity.class.isAssignableFrom(firstParameterType)) {
return Stream.empty();
}
return BaseEntityTest_Utils.getEntityInstanceStream(
firstParameterType.asSubclass(BaseEntity.class)
).map(Arguments::of);
}
}
Now the firstParameterType
is always BaseEntity.class
. How can I get the actual type of the ENTITY
within the BaseEntityArgumentsProvider
class?
Given you have parent:
public class BaseEntity<SELF> {
}
Then you have a child:
public class ChildEntity extends BaseEntity<ChildEntity> {
}
Then you can have such abstract test:
abstract class ParentTest<SELF extends BaseEntity<SELF>> {
@Test
void test() {
assertEquals(ChildEntity.class,
((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]);
}
}
And its concrete child:
public class ChildTest extends ParentTest<ChildEntity> {
}
So the key part here is to use reflection to have a type of a generic:
((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]
Explanation:
child gets its superclass: var genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass()
;
child gets the type of the first generic:
genericSuperclass.getActualTypeArguments()[0]
.
UPD: you can also extract this magic-like reflection logic to an utils class:
public class GenericsUtils {
public static Class<?> getGenericType(Object object) {
return (Class<?>) ((ParameterizedType) object.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
So you would have this test body:
assertEquals(ChildEntity.class, GenericsUtils.getGenericType(this));
Source: