diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 3d69e9fd92c2..999fc480cc0b 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -866,6 +866,24 @@ public Class[] resolveGenerics(Class fallback) { return getGeneric(indexes).resolve(); } + /** + * Convenience method that will {@link #getGeneric(int...) get} and + * {@link #resolve() resolve} a specific generic parameter. + * @param indexes the indexes that refer to the generic parameter + * (can be omitted to return the first generic) + * @return a resolved {@link Class} + * @throws IllegalStateException if no generic was available + * @see #resolveGeneric(int...) + * @see #getGeneric(int...) + * @see #resolve() + */ + @SuppressWarnings("unchecked") + public Class resolveRequiredGeneric(int... indexes) { + Class generic = getGeneric(indexes).resolve(); + Assert.state(generic != null, "The generic must not be null"); + return (Class) generic; + } + /** * Resolve this type to a {@link java.lang.Class}, returning {@code null} * if the type cannot be resolved. This method will consider bounds of diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index f6e66a2c0b57..fc0aa63ac80c 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -55,6 +55,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.atLeastOnce; @@ -1601,6 +1602,14 @@ void gh34541() throws Exception { assertThat(typeWithGenerics.isAssignableFrom(PaymentCreator.class)).isTrue(); } + @Test + void resolveRequiredGeneric() { + assertThat(new AbstractRepository() {}.entityClass).isEqualTo(String.class); + assertThat(new AbstractRepository() {}.entityClass).isEqualTo(Long.class); + assertThat(new AbstractRepository<>() {}.entityClass).isEqualTo(Object.class); + assertThatIllegalStateException().isThrownBy(() -> new AbstractRepository() {}); + } + private ResolvableType testSerialization(ResolvableType type) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -2013,6 +2022,14 @@ static class PaymentCreatorParameter { abstract static class Payment { } + abstract class AbstractRepository { + + final Class entityClass; + + AbstractRepository() { + entityClass = ResolvableType.forClass(getClass()).as(AbstractRepository.class).resolveRequiredGeneric(); + } + } private static class ResolvableTypeAssert extends AbstractAssert{