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


The following commit(s) were added to refs/heads/master by this push:
     new d62646495 [LANG-1323] Align built type hashes with JDK reflection 
(#1826)
d62646495 is described below

commit d626464952b57e665a83bf556cd0f0e7fd9983fb
Author: agamal026 <[email protected]>
AuthorDate: Tue Oct 20 00:29:42 2026 +0200

    [LANG-1323] Align built type hashes with JDK reflection (#1826)
    
    * [LANG-1323] Add reflection type hash interoperability regressions
    
    Compare built parameterized and generic array types with JDK reflection,
    including nested arguments and owner types. Exercise symmetric equality,
    hash equality, and HashMap/HashSet lookup in both directions.
    
    Generated-by: OpenAI ChatGPT (GPT-6 Astra)
    
    * [LANG-1323] Align built type hashes with JDK reflection
    
    Use the component type hash for generic arrays and combine the argument,
    owner and raw type hashes for parameterized types. Equal built and JDK
    reflection types can then be used interchangeably as hash collection keys.
    
    The 14 new regressions failed against unchanged production and pass with
    this fix. The complete TypeUtilsTest run reports 510 tests, zero failures
    or errors, and one existing disabled test. Preserve the wildcard changes
    from #1807; this completes the two separate type kinds identified there.
    
    Generated-by: OpenAI ChatGPT (GPT-6 Astra)
---
 .../apache/commons/lang3/reflect/TypeUtils.java    | 14 ++---
 .../commons/lang3/reflect/TypeUtilsTest.java       | 65 ++++++++++++++++++++++
 2 files changed, 69 insertions(+), 10 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 c30e0f264..852283424 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
@@ -86,9 +86,8 @@ public Type getGenericComponentType() {
          */
         @Override
         public int hashCode() {
-            int result = 67 << 4;
-            result |= componentType.hashCode();
-            return result;
+            // Match the JDK's hash code for an equal generic array type.
+            return componentType.hashCode();
         }
 
         /**
@@ -158,13 +157,8 @@ public Type getRawType() {
          */
         @Override
         public int hashCode() {
-            int result = 71 << 4;
-            result |= raw.hashCode();
-            result <<= 4;
-            result |= Objects.hashCode(useOwner);
-            result <<= 8;
-            result |= Arrays.hashCode(typeArguments);
-            return result;
+            // Match the JDK's hash code for an equal parameterized type.
+            return Arrays.hashCode(typeArguments) ^ Objects.hashCode(useOwner) 
^ raw.hashCode();
         }
 
         /**
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 4a9bea245..f145a0fab 100644
--- a/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java
@@ -18,6 +18,7 @@
 
 import static 
org.apache.commons.lang3.LangAssertions.assertIllegalArgumentException;
 import static 
org.apache.commons.lang3.LangAssertions.assertNullPointerException;
+import static org.junit.jupiter.api.Assertions.assertAll;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -45,6 +46,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -351,6 +353,23 @@ static class StaticMember implements TestIF<Number> {
         }
     }
 
+    private static class HashCodeTypes<T> {
+        T[] array;
+        List<T[]> arrayArgument;
+        Map.Entry<String, Integer> entry;
+        List<String> list;
+        List<String>[] listArray;
+        Map<String, Integer> map;
+        Outer<String>.InnerU<Integer> member;
+        Map<String, List<Integer>> nested;
+        T[][] nestedArray;
+        List<String>[][] nestedListArray;
+        ParameterizedOwner<String>.NonGenericInner nonGenericMember;
+        List<T> variableList;
+        List<? super T[]> wildcardArrayArgument;
+        List<?> wildcardList;
+    }
+
     static class LexOrdering<T> extends MyOrdering<Iterable<T>> implements 
Serializable {
         private static final long serialVersionUID = 1L;
     }
@@ -484,6 +503,29 @@ public static <T extends Comparable<? extends T>> T 
stub3() {
         return null;
     }
 
+    static Stream<Arguments> testHashCodeMatchesJdk() {
+        final Type variable = HashCodeTypes.class.getTypeParameters()[0];
+        final Type array = TypeUtils.genericArrayType(variable);
+        final Type strings = TypeUtils.parameterize(List.class, String.class);
+        return Stream.of(
+                Arguments.of("array", array),
+                Arguments.of("arrayArgument", 
TypeUtils.parameterize(List.class, array)),
+                Arguments.of("entry", 
TypeUtils.parameterizeWithOwner(Map.class, Map.Entry.class, String.class, 
Integer.class)),
+                Arguments.of("list", strings),
+                Arguments.of("listArray", TypeUtils.genericArrayType(strings)),
+                Arguments.of("map", TypeUtils.parameterize(Map.class, 
String.class, Integer.class)),
+                Arguments.of("member", 
TypeUtils.parameterizeWithOwner(TypeUtils.parameterize(Outer.class, 
String.class),
+                        Outer.InnerU.class, Integer.class)),
+                Arguments.of("nested", TypeUtils.parameterize(Map.class, 
String.class, TypeUtils.parameterize(List.class, Integer.class))),
+                Arguments.of("nestedArray", TypeUtils.genericArrayType(array)),
+                Arguments.of("nestedListArray", 
TypeUtils.genericArrayType(TypeUtils.genericArrayType(strings))),
+                Arguments.of("nonGenericMember", 
TypeUtils.parameterizeWithOwner(TypeUtils.parameterize(ParameterizedOwner.class,
 String.class),
+                        ParameterizedOwner.NonGenericInner.class)),
+                Arguments.of("variableList", 
TypeUtils.parameterize(List.class, variable)),
+                Arguments.of("wildcardArrayArgument", 
TypeUtils.parameterize(List.class, 
TypeUtils.wildcardType().withLowerBounds(array).build())),
+                Arguments.of("wildcardList", 
TypeUtils.parameterize(List.class, TypeUtils.WILDCARD_ALL)));
+    }
+
     static Stream<Arguments> testIsAssignable_ClassWildcardBounds() {
         return Stream.of(
                 Arguments.of("arrayValues", "extendsStringArrays", true),
@@ -1217,6 +1259,29 @@ void testIsArrayGenericTypes() throws 
NoSuchFieldException {
         assertTrue(TypeUtils.isArrayType(superStringListTypeArray));
     }
 
+    @ParameterizedTest
+    @MethodSource
+    void testHashCodeMatchesJdk(final String fieldName, final Type built) 
throws NoSuchFieldException {
+        final Type jdk = 
HashCodeTypes.class.getDeclaredField(fieldName).getGenericType();
+        assertEquals(jdk, built);
+        assertEquals(built, jdk);
+
+        final Map<Type, String> jdkMap = new HashMap<>();
+        jdkMap.put(jdk, "value");
+        final Map<Type, String> builtMap = new HashMap<>();
+        builtMap.put(built, "value");
+        final Set<Type> jdkSet = new HashSet<>();
+        jdkSet.add(jdk);
+        final Set<Type> builtSet = new HashSet<>();
+        builtSet.add(built);
+        assertAll(
+                () -> assertEquals(jdk.hashCode(), built.hashCode()),
+                () -> assertEquals("value", jdkMap.get(built)),
+                () -> assertEquals("value", builtMap.get(jdk)),
+                () -> assertTrue(jdkSet.contains(built)),
+                () -> assertTrue(builtSet.contains(jdk)));
+    }
+
     @Test
     void testIsArrayTypeClasses() {
         assertTrue(TypeUtils.isArrayType(boolean[].class));

Reply via email to