diff --git a/spring-core/src/main/java/org/springframework/aot/generate/ValueCodeGeneratorDelegates.java b/spring-core/src/main/java/org/springframework/aot/generate/ValueCodeGeneratorDelegates.java
index caa806a446..86904a0c30 100644
--- a/spring-core/src/main/java/org/springframework/aot/generate/ValueCodeGeneratorDelegates.java
+++ b/spring-core/src/main/java/org/springframework/aot/generate/ValueCodeGeneratorDelegates.java
@@ -331,7 +331,7 @@ public abstract class ValueCodeGeneratorDelegates {
return CodeBlock.of("$T.NONE", ResolvableType.class);
}
Class> type = ClassUtils.getUserClass(resolvableType.toClass());
- if (resolvableType.hasGenerics() && !resolvableType.hasUnresolvableGenerics()) {
+ if (resolvableType.hasGenerics() && resolvableType.hasResolvableGenerics()) {
return generateCodeWithGenerics(resolvableType, type);
}
if (allowClassResult) {
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 055b41dc01..a5e0d74c65 100644
--- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
+++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
@@ -138,7 +138,7 @@ public final class GenericTypeResolver {
@Nullable
public static Class>[] resolveTypeArguments(Class> clazz, Class> genericType) {
ResolvableType type = ResolvableType.forClass(clazz).as(genericType);
- if (!type.hasGenerics() || type.isEntirelyUnresolvable()) {
+ if (!type.hasGenerics() || !type.hasResolvableGenerics()) {
return null;
}
return type.resolveGenerics(Object.class);
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 66b5b9509b..ed541ebbda 100644
--- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java
+++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java
@@ -568,20 +568,23 @@ public class ResolvableType implements Serializable {
}
/**
- * Return {@code true} if this type contains unresolvable generics only,
- * that is, no substitute for any of its declared type variables.
+ * Return {@code true} if this type contains at least a generic type
+ * that is resolved. In other words, this returns {@code false} if
+ * the type contains unresolvable generics only, that is, no substitute
+ * for any of its declared type variables.
+ * @since 6.2
*/
- boolean isEntirelyUnresolvable() {
+ public boolean hasResolvableGenerics() {
if (this == NONE) {
return false;
}
ResolvableType[] generics = getGenerics();
for (ResolvableType generic : generics) {
if (!generic.isUnresolvableTypeVariable() && !generic.isWildcardWithoutBounds()) {
- return false;
+ return true;
}
}
- return true;
+ return false;
}
/**
diff --git a/spring-core/src/test/java/org/springframework/aot/generate/ValueCodeGeneratorTests.java b/spring-core/src/test/java/org/springframework/aot/generate/ValueCodeGeneratorTests.java
index dced0ed7bb..dc755467ec 100644
--- a/spring-core/src/test/java/org/springframework/aot/generate/ValueCodeGeneratorTests.java
+++ b/spring-core/src/test/java/org/springframework/aot/generate/ValueCodeGeneratorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -290,6 +290,34 @@ class ValueCodeGeneratorTests {
+ "ResolvableType.forClassWithGenerics(List.class, String.class))");
}
+ @Test
+ void generateWhenUnresolvedGenericType() throws NoSuchFieldException {
+ ResolvableType resolvableType = ResolvableType
+ .forField(SampleTypes.class.getField("genericList"));
+ assertThat(resolve(generateCode(resolvableType)))
+ .hasImport(ResolvableType.class, List.class)
+ .hasValueCode("ResolvableType.forClass(List.class)");
+ }
+
+ @Test
+ void generateWhenUnresolvedNestedGenericType() throws NoSuchFieldException {
+ ResolvableType resolvableType = ResolvableType
+ .forField(SampleTypes.class.getField("mapWithNestedGenericInValueType"));
+ assertThat(resolve(generateCode(resolvableType)))
+ .hasImport(ResolvableType.class, List.class)
+ .hasValueCode("""
+ ResolvableType.forClassWithGenerics(Map.class, ResolvableType.forClass(String.class), \
+ ResolvableType.forClass(List.class))""");
+ }
+
+ static class SampleTypes {
+
+ public List genericList;
+
+ public Map> mapWithNestedGenericInValueType;
+
+ }
+
}
@Nested
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 8e8882b7b7..e2f397528a 100644
--- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
+++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
@@ -1289,6 +1289,30 @@ class ResolvableTypeTests {
assertThat(narrow.getGeneric().resolve()).isEqualTo(String.class);
}
+ @Test
+ void hasResolvableGenerics() throws Exception {
+ ResolvableType type = ResolvableType.forField(Fields.class.getField("stringList"));
+ assertThat(type.hasResolvableGenerics()).isTrue();
+ }
+
+ @Test
+ void hasResolvableGenericsWithSingleBoundedWildcard() throws Exception {
+ ResolvableType type = ResolvableType.forField(Fields.class.getField("wildcardType"));
+ assertThat(type.hasResolvableGenerics()).isTrue();
+ }
+
+ @Test
+ void hasResolvableGenericsWithSingleParameterizedType() throws Exception {
+ ResolvableType type = ResolvableType.forField(Fields.class.getField("parameterizedType"));
+ assertThat(type.hasResolvableGenerics()).isFalse();
+ }
+
+ @Test
+ void hasResolvableGenericsWithSingleWildcard() throws Exception {
+ ResolvableType type = ResolvableType.forField(Fields.class.getField("anyListElement"));
+ assertThat(type.hasResolvableGenerics()).isFalse();
+ }
+
@Test
void hasUnresolvableGenerics() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringList"));
@@ -1466,6 +1490,8 @@ class ResolvableTypeTests {
public List[][][] genericMultiArrayType;
+ public List> anyListElement;
+
public List extends Number> wildcardType;
public List super Number> wildcardSuperType = new ArrayList