This is an automated email from the ASF dual-hosted git repository. garydgregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-lang.git
commit 8d428af792181866a8500febc3526dc13987bdba Author: Gary Gregory <[email protected]> AuthorDate: Thu Oct 15 15:45:53 2026 -0400 Strengthen TypeUtils substitution tests and Javadoc Clarify generic array substitution and return values in unrollVariables(). Add direct tests for inherited array arguments and wildcard bounds, plus coverage for cyclic array assignments and self-referencing generic arrays. --- .../apache/commons/lang3/reflect/TypeUtils.java | 10 +++-- .../commons/lang3/reflect/TypeUtilsTest.java | 49 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java b/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java index 173d99c33..c30e0f264 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java @@ -1865,13 +1865,15 @@ private static Type unrollVariableAssignments(TypeVariable<?> typeVariable, fina /** * Gets a type representing {@code type} with variable assignments "unrolled." - * - * <p>Substitutes variables in parameterized types, wildcard bounds, and generic array components. If an array component resolves to a class, the result - * is the corresponding array class.</p> + * <p> + * Substitutes variables in parameterized types, wildcard bounds, and generic array components. If substituting a generic array component produces a class, + * the result is the corresponding array class. + * </p> * * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)}. * @param type The type to unroll variable assignments for. - * @return Type. + * @return The type with variable assignments substituted, or {@code null} if the supplied type is {@code null} or a type variable with no resolvable + * assignment. * @since 3.2 */ public static Type unrollVariables(Map<TypeVariable<?>, Type> typeArguments, final Type type) { diff --git a/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java index 284ddddea..4a9bea245 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java @@ -1148,6 +1148,34 @@ void testGetTypeArguments() { assertEquals(getClass().getTypeParameters()[0], typeVarAssigns.get(Other.class.getTypeParameters()[0])); } + @Test + void testGetTypeArgumentsInheritedGenericArray() { + final TypeVariable<?> variable = ClassWildcardTypes.class.getTypeParameters()[1]; + final TypeVariable<?> listVariable = List.class.getTypeParameters()[0]; + final Type source = TypeUtils.parameterize(ArrayValues.class, variable); + final Map<TypeVariable<?>, Type> arguments = TypeUtils.getTypeArguments(source, List.class); + final GenericArrayType array = (GenericArrayType) arguments.get(listVariable); + assertSame(variable, array.getGenericComponentType()); + assertEquals(String[].class, + TypeUtils.getTypeArguments(TypeUtils.parameterize(ArrayValues.class, String.class), List.class).get(listVariable)); + } + + @Test + void testGetTypeArgumentsInheritedWildcardBounds() { + final TypeVariable<?> variable = ClassWildcardTypes.class.getTypeParameters()[1]; + final TypeVariable<?> listVariable = List.class.getTypeParameters()[0]; + final WildcardType upper = TypeUtils.wildcardType().withUpperBounds(variable).build(); + final WildcardType lower = TypeUtils.wildcardType().withLowerBounds(variable).build(); + final WildcardType inheritedUpper = (WildcardType) TypeUtils.getTypeArguments(TypeUtils.parameterize(ArrayList.class, upper), List.class) + .get(listVariable); + final WildcardType inheritedLower = (WildcardType) TypeUtils.getTypeArguments(TypeUtils.parameterize(ArrayList.class, lower), List.class) + .get(listVariable); + assertArrayEquals(new Type[] { variable }, inheritedUpper.getUpperBounds()); + assertArrayEquals(new Type[0], inheritedUpper.getLowerBounds()); + assertArrayEquals(new Type[] { Object.class }, inheritedLower.getUpperBounds()); + assertArrayEquals(new Type[] { variable }, inheritedLower.getLowerBounds()); + } + @Test void testInterfaceBoundPreserved() { assertEquals("T extends java.util.List<java.lang.String>", @@ -2019,6 +2047,27 @@ void testUnrollVariablesGenericArray() { TypeUtils.unrollVariables(Collections.singletonMap(variable, String.class), TypeUtils.genericArrayType(array))); } + @Test + void testUnrollVariablesGenericArrayCyclicAssignment() { + final TypeVariable<?> variable = ArrayValues.class.getTypeParameters()[0]; + final GenericArrayType array = TypeUtils.genericArrayType(variable); + final Map<TypeVariable<?>, Type> mapping = Collections.singletonMap(variable, array); + assertSame(array, TypeUtils.unrollVariables(mapping, variable)); + assertSame(variable, array.getGenericComponentType()); + } + + @Test + void testUnrollVariablesGenericArraySelfReference() { + final GenericArrayType array = new GenericArrayType() { + @Override + public Type getGenericComponentType() { + return this; + } + }; + assertSame(array, TypeUtils.unrollVariables(Collections.emptyMap(), array)); + assertSame(array, array.getGenericComponentType()); + } + @Test void testUnrollVariablesMultiLevelOwnerType() throws NoSuchFieldException { final TypeVariable<?> t = Outer.class.getTypeParameters()[0];
