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];

Reply via email to