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 wildcardType; public List wildcardSuperType = new ArrayList();