diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java index 24394da9da..a28765c50b 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -199,8 +199,13 @@ public final class GenericTypeResolver { private static ResolvableType resolveVariable(TypeVariable typeVariable, ResolvableType contextType) { ResolvableType resolvedType; if (contextType.hasGenerics()) { - resolvedType = ResolvableType.forType(typeVariable, contextType); - if (resolvedType.resolve() != null) { + ResolvableType.VariableResolver variableResolver = contextType.asVariableResolver(); + if (variableResolver == null) { + return ResolvableType.NONE; + } + + resolvedType = variableResolver.resolveVariable(typeVariable); + if (resolvedType != null) { return resolvedType; } } @@ -208,16 +213,17 @@ public final class GenericTypeResolver { ResolvableType superType = contextType.getSuperType(); if (superType != ResolvableType.NONE) { resolvedType = resolveVariable(typeVariable, superType); - if (resolvedType.resolve() != null) { + if (resolvedType != ResolvableType.NONE) { return resolvedType; } } for (ResolvableType ifc : contextType.getInterfaces()) { resolvedType = resolveVariable(typeVariable, ifc); - if (resolvedType.resolve() != null) { + if (resolvedType != ResolvableType.NONE) { return resolvedType; } } + return ResolvableType.NONE; } diff --git a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java index 80eee82c84..b2019af524 100644 --- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java @@ -173,6 +173,19 @@ class GenericTypeResolverTests { assertThat(resolved[1]).isEqualTo(Long.class); } + @Test + public void resolvePartiallySpecializedTypeVariables() { + Type resolved = resolveType(BiGenericClass.class.getTypeParameters()[0], TypeFixedBiGenericClass.class); + assertThat(resolved).isNotNull(); + assertThat(D.class).isEqualTo(resolved); + } + + @Test + public void resolveTransitiveTypeVariableWithDifferentName() { + Type resolved = resolveType(BiGenericClass.class.getTypeParameters()[1], TypeFixedBiGenericClass.class); + assertThat(resolved).isNotNull(); + assertThat(E.class).isEqualTo(resolved); + } public interface MyInterfaceType { } @@ -293,11 +306,23 @@ class GenericTypeResolverTests { class B{} + class C extends A {} + + class D extends B {} + + class E extends C {} + class TestIfc{} class TestImpl> extends TestIfc{ } + static abstract class BiGenericClass, V extends A> {} + + static abstract class SpecializedBiGenericClass extends BiGenericClass{} + + static class TypeFixedBiGenericClass extends SpecializedBiGenericClass {} + static class TopLevelClass { class Nested { }