This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 46f0e80533 New BeanCreator API
46f0e80533 is described below

commit 46f0e80533dd4aa89fd38625924e03267332b27f
Author: James Bognar <[email protected]>
AuthorDate: Wed Jan 21 16:51:43 2026 -0500

    New BeanCreator API
---
 .../apache/juneau/commons/reflect/MethodInfo.java  |   4 +-
 .../juneau/commons/utils/CollectionUtils.java      |  30 +++
 .../org/apache/juneau/commons/utils/Utils.java     |  72 ++++++-
 .../juneau/commons/reflect/ClassInfo_Test.java     | 214 +++++++++++++++++++++
 .../juneau/commons/reflect/MethodInfo_Test.java    |  96 +++++++++
 .../apache/juneau/commons/utils/Utils_Test.java    |  49 +++++
 6 files changed, 455 insertions(+), 10 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
index b3b18938b9..529ecf2395 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/MethodInfo.java
@@ -188,9 +188,9 @@ public class MethodInfo extends ExecutableInfo implements 
Comparable<MethodInfo>
                if (i == 0) {
                        var declaringClass = getDeclaringClass();
                        var oDeclaringClass = o.getDeclaringClass();
-                       if (declaringClass.isParentOf(oDeclaringClass)) {
+                       if (declaringClass.isChildOf(oDeclaringClass)) {
                                i = -1; // This method is from a child class, 
so it comes first
-                       } else if (oDeclaringClass.isParentOf(declaringClass)) {
+                       } else if (oDeclaringClass.isChildOf(declaringClass)) {
                                i = 1; // Other method is from a child class, 
so it comes first
                        } else {
                                i = cmp(declaringClass.getName(), 
oDeclaringClass.getName()); // Neither is a child of the other, compare by 
class name for deterministic ordering
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
index d08269d10b..6a2a3e5eac 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
@@ -852,6 +852,36 @@ public class CollectionUtils {
                return e(l) ? null : l.get(l.size() - 1);
        }
 
+       /**
+        * Returns the element at the specified index.
+        *
+        * <p>
+        * This is a null-safe convenience method that safely accesses list 
elements.
+        * If the list is <jk>null</jk>, the index is negative, or the index is 
greater than or equal to the list size,
+        * this method returns <jk>null</jk> instead of throwing an exception.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      List&lt;String&gt; <jv>list</jv> = <jsm>l</jsm>(<js>"a"</js>, 
<js>"b"</js>, <js>"c"</js>);
+        *
+        *      String <jv>first</jv> = <jsm>at</jsm>(<jv>list</jv>, 0);   
<jc>// Returns "a"</jc>
+        *      String <jv>second</jv> = <jsm>at</jsm>(<jv>list</jv>, 1);  
<jc>// Returns "b"</jc>
+        *      String <jv>outOfBounds</jv> = <jsm>at</jsm>(<jv>list</jv>, 10); 
 <jc>// Returns null</jc>
+        *      String <jv>negative</jv> = <jsm>at</jsm>(<jv>list</jv>, -1);  
<jc>// Returns null</jc>
+        *      String <jv>nullList</jv> = <jsm>at</jsm>(<jk>null</jk>, 0);  
<jc>// Returns null</jc>
+        * </p>
+        *
+        * @param <E> The element type.
+        * @param l The list. Can be <jk>null</jk>.
+        * @param index The index to access.
+        * @return The element at the specified index, or <jk>null</jk> if the 
list is <jk>null</jk>, the index is out of bounds, or negative.
+        */
+       public static <E> E at(List<E> l, int index) {
+               if (l == null || index < 0 || index >= l.size())
+                       return null;
+               return l.get(index);
+       }
+
        /**
         * Returns the length of the specified array.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
index 1fa5a3df69..89a018c3ae 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/Utils.java
@@ -501,14 +501,14 @@ public class Utils {
         * Tests two objects for equality, gracefully handling nulls and arrays.
         *
         * <p>
- * This method handles annotations specially by delegating to {@link 
org.apache.juneau.commons.utils.AnnotationUtils#equals(java.lang.annotation.Annotation,
 java.lang.annotation.Annotation)}
- * to ensure proper annotation comparison according to the annotation equality 
contract.
- *
- * @param <T> The value types.
- * @param o1 Object 1.
- * @param o2 Object 2.
- * @return <jk>true</jk> if both objects are equal based on the {@link 
Object#equals(Object)} method.
- * @see 
org.apache.juneau.commons.utils.AnnotationUtils#equals(java.lang.annotation.Annotation,
 java.lang.annotation.Annotation)
+        * This method handles annotations specially by delegating to {@link 
org.apache.juneau.commons.utils.AnnotationUtils#equals(java.lang.annotation.Annotation,
 java.lang.annotation.Annotation)}
+        * to ensure proper annotation comparison according to the annotation 
equality contract.
+        *
+        * @param <T> The value types.
+        * @param o1 Object 1.
+        * @param o2 Object 2.
+        * @return <jk>true</jk> if both objects are equal based on the {@link 
Object#equals(Object)} method.
+        * @see 
org.apache.juneau.commons.utils.AnnotationUtils#equals(java.lang.annotation.Annotation,
 java.lang.annotation.Annotation)
         */
        public static <T> boolean eq(T o1, T o2) {
                // Handle annotations specially
@@ -527,6 +527,37 @@ public class Utils {
                return Objects.equals(o1, o2);
        }
 
+       /**
+        * Returns <jk>true</jk> if the first argument equals any of the 
varargs arguments.
+        *
+        * <p>
+        * Uses {@link #eq(Object, Object)} for equality comparison, which 
handles nulls, arrays, and annotations gracefully.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      <jk>assertTrue</jk>(eqAny(<js>"apple"</js>, <js>"apple"</js>, 
<js>"banana"</js>, <js>"cherry"</js>));
+        *      <jk>assertFalse</jk>(eqAny(<js>"apple"</js>, <js>"banana"</js>, 
<js>"cherry"</js>));
+        *      <jk>assertTrue</jk>(eqAny(<jk>null</jk>, <js>"apple"</js>, 
<jk>null</jk>, <js>"banana"</js>));
+        *      <jk>assertFalse</jk>(eqAny(<js>"apple"</js>));  <jc>// Empty 
varargs</jc>
+        * </p>
+        *
+        * @param <T> The value types.
+        * @param o1 The first object to compare.
+        * @param o2 The varargs array of objects to compare against.
+        * @return <jk>true</jk> if <c>o1</c> equals any of the values in 
<c>o2</c>, <jk>false</jk> otherwise.
+        *      Returns <jk>false</jk> if <c>o2</c> is <jk>null</jk> or empty.
+        * @see #eq(Object, Object)
+        */
+       @SafeVarargs
+       public static <T> boolean eqAny(T o1, T...o2) {
+               if (o2 == null || o2.length == 0)
+                       return false;
+               for (var o : o2)
+                       if (eq(o1, o))
+                               return true;
+               return false;
+       }
+
        /**
         * Tests two objects for equality using a custom predicate, gracefully 
handling nulls.
         *
@@ -1557,6 +1588,31 @@ public class Utils {
                return Optional.ofNullable(t);
        }
 
+       /**
+        * Returns an Optional containing the element at the specified index in 
the list, or empty if the index is out of bounds or the element is 
<jk>null</jk>.
+        *
+        * <p>
+        * This is a convenience method that combines {@link 
CollectionUtils#at(List, int)} with {@link #opt(Object)}.
+        * </p>
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      List&lt;String&gt; <jv>list</jv> = 
<jsm>list</jsm>(<js>"a"</js>, <js>"b"</js>, <js>"c"</js>);
+        *      Optional&lt;String&gt; <jv>result</jv> = 
<jsm>opt</jsm>(<jv>list</jv>, 1);  <jc>// Optional.of("b")</jc>
+        *      Optional&lt;String&gt; <jv>result2</jv> = 
<jsm>opt</jsm>(<jv>list</jv>, 10);  <jc>// Optional.empty()</jc>
+        * </p>
+        *
+        * @param <T> The element type.
+        * @param l The list to get the element from.
+        * @param index The index of the element to retrieve.
+        * @return An Optional containing the element at the specified index, 
or empty if the index is out of bounds or the element is <jk>null</jk>.
+        * @see CollectionUtils#at(List, int)
+        * @see #opt(Object)
+        */
+       public static final <T> Optional<T> opt(List<T> l, int index) {
+               return opt(CollectionUtils.at(l, index));
+       }
+
        /**
         * Returns an empty Optional.
         *
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
index 3629843542..917047be75 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/ClassInfo_Test.java
@@ -1999,6 +1999,28 @@ public class ClassInfo_Test extends TestBase {
                assertFalse(method2.isPresent());
        }
 
+       // Test classes for bridge method testing
+       public interface GenericInterfaceForBridge<T> {
+               T getValue();
+       }
+       public static class BridgeMethodTestClass implements 
GenericInterfaceForBridge<String> {
+               @Override
+               public String getValue() { return "value"; }  // This creates a 
bridge method
+       }
+
+       // Test classes for covariant return type (creates bridge method)
+       public static class ParentWithMethod {
+               public ParentWithMethod getInstance() {
+                       return new ParentWithMethod();
+               }
+       }
+       public static class ChildWithCovariantReturn extends ParentWithMethod {
+               @Override
+               public ChildWithCovariantReturn getInstance() {
+                       return new ChildWithCovariantReturn();
+               }
+       }
+
        
//====================================================================================================
        // getPublicMethods()
        
//====================================================================================================
@@ -2014,6 +2036,35 @@ public class ClassInfo_Test extends TestBase {
                check("", pTypeGenericArgInfo.getPublicMethods());
        }
 
+       /**
+        * Tests that getPublicMethods() excludes both bridge and synthetic 
methods.
+        * Some methods can be both bridge and synthetic, and they should be 
excluded.
+        */
+       @Test
+       void a059d_getPublicMethods_excludesBridgeAndSyntheticMethods() {
+               // Test with ArrayList which implements List and has bridge 
methods
+               var arrayListInfo = ClassInfo.of(java.util.ArrayList.class);
+               var publicMethods = arrayListInfo.getPublicMethods();
+
+               // Verify no bridge methods are included
+               var bridgeMethods = publicMethods.stream()
+                       .filter(MethodInfo::isBridge)
+                       .toList();
+               assertTrue(bridgeMethods.isEmpty(), "getPublicMethods() should 
not include bridge methods. Found: " + bridgeMethods);
+
+               // Verify no synthetic methods are included
+               var syntheticMethods = publicMethods.stream()
+                       .filter(MethodInfo::isSynthetic)
+                       .toList();
+               assertTrue(syntheticMethods.isEmpty(), "getPublicMethods() 
should not include synthetic methods. Found: " + syntheticMethods);
+
+               // Verify that methods that are both bridge and synthetic are 
excluded
+               var bridgeOrSynthetic = publicMethods.stream()
+                       .filter(m -> m.isBridge() || m.isSynthetic())
+                       .toList();
+               assertTrue(bridgeOrSynthetic.isEmpty(), "getPublicMethods() 
should not include methods that are bridge or synthetic. Found: " + 
bridgeOrSynthetic);
+       }
+
        
//====================================================================================================
        // getRecordComponents()
        
//====================================================================================================
@@ -3548,6 +3599,10 @@ public class ClassInfo_Test extends TestBase {
        @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, 
java.lang.annotation.ElementType.METHOD})
        @interface Autowired {}
 
+       
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+       @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
+       @interface PostConstruct {}
+
        // Test classes for field injection
        static class TestFieldInjection {
                @Inject
@@ -3665,6 +3720,84 @@ public class ClassInfo_Test extends TestBase {
                }
        }
 
+       // Test class for PostConstruct injection
+       static class TestPostConstruct {
+               boolean postConstructCalled = false;
+               TestService injectedService;
+               boolean postConstruct2Called = false;
+
+               @Inject
+               TestService service;
+
+               @PostConstruct
+               void postConstruct() {
+                       postConstructCalled = true;
+                       // Verify injection happened before PostConstruct
+                       injectedService = service;
+               }
+
+               @PostConstruct
+               void postConstruct2() {
+                       postConstruct2Called = true;
+               }
+       }
+
+       // Parent class with PostConstruct for testing parent-to-child order
+       static class ParentWithPostConstruct {
+               final List<String> callOrder = new ArrayList<>();
+
+               @PostConstruct
+               void parentPostConstruct() {
+                       callOrder.add("parent");
+               }
+       }
+
+       // Child class with PostConstruct for testing parent-to-child order
+       static class ChildWithPostConstruct extends ParentWithPostConstruct {
+               @PostConstruct
+               void childPostConstruct() {
+                       callOrder.add("child");
+               }
+       }
+
+       // Test class with PostConstruct that has parameters (should be skipped)
+       static class TestPostConstructWithParams {
+               boolean called = false;
+
+               @PostConstruct
+               void postConstruct(TestService service) {
+                       called = true; // Should not be called - has parameters
+               }
+       }
+
+       // Test class with PostConstruct that returns value (should still be 
called)
+       static class TestPostConstructWithReturn {
+               boolean called = false;
+
+               @PostConstruct
+               public String postConstruct() {
+                       called = true;
+                       return "result"; // Return value should be ignored
+               }
+       }
+
+       // Test class with abstract PostConstruct method (should be skipped)
+       static abstract class TestAbstractPostConstruct {
+               @PostConstruct
+               abstract void postConstruct();
+       }
+
+       // Test class with PostConstruct and type parameters (should be skipped)
+       static class TestPostConstructWithTypeParams {
+               boolean called = false;
+
+               @PostConstruct
+               @SuppressWarnings("unused")
+               <T> void postConstruct() {
+                       called = true; // Should not be called - has type 
parameters
+               }
+       }
+
        // Abstract test class for testing abstract method skipping
        static abstract class TestAbstractMethodInjection {
                @Inject
@@ -4093,5 +4226,86 @@ public class ClassInfo_Test extends TestBase {
                assertSame(service, combined.field);
                // Method should have been called (no exception means it worked)
        }
+
+       
//====================================================================================================
+       // injectBeans - PostConstruct tests
+       
//====================================================================================================
+
+       @Test
+       void b030_injectBeans_postConstructCalled() {
+               var service = new TestService("test1");
+               beanStore.addBean(TestService.class, service);
+               var bean = new TestPostConstruct();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               assertTrue(bean.postConstructCalled, "@PostConstruct method 
should be called");
+               assertSame(service, bean.injectedService, "Injection should 
happen before PostConstruct");
+       }
+
+       @Test
+       void b031_injectBeans_postConstructCalledAfterInjection() {
+               var service = new TestService("test1");
+               beanStore.addBean(TestService.class, service);
+               var bean = new TestPostConstruct();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               // Verify that injection happened before PostConstruct
+               assertNotNull(bean.injectedService, "Service should be injected 
before PostConstruct runs");
+               assertSame(service, bean.injectedService);
+               assertTrue(bean.postConstructCalled);
+       }
+
+       @Test
+       void b032_injectBeans_postConstructMultipleMethods() {
+               var service = new TestService("test1");
+               beanStore.addBean(TestService.class, service);
+               var bean = new TestPostConstruct();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               assertTrue(bean.postConstructCalled, "First @PostConstruct 
method should be called");
+               assertTrue(bean.postConstruct2Called, "Second @PostConstruct 
method should be called");
+       }
+
+       @Test
+       void b033_injectBeans_postConstructParentToChildOrder() {
+               var bean = new ChildWithPostConstruct();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               // Parent PostConstruct should be called before child 
PostConstruct
+               assertEquals(2, bean.callOrder.size());
+               assertEquals("parent", bean.callOrder.get(0), "Parent 
PostConstruct should be called first");
+               assertEquals("child", bean.callOrder.get(1), "Child 
PostConstruct should be called second");
+       }
+
+       @Test
+       void b034_injectBeans_postConstructWithParametersSkipped() {
+               var service = new TestService("test1");
+               beanStore.addBean(TestService.class, service);
+               var bean = new TestPostConstructWithParams();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               assertFalse(bean.called, "@PostConstruct method with parameters 
should be skipped");
+       }
+
+       @Test
+       void b035_injectBeans_postConstructWithReturnValue() {
+               var bean = new TestPostConstructWithReturn();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               assertFalse(bean.called, "@PostConstruct method with return 
value should be skipped");
+       }
+
+       @Test
+       void b036_injectBeans_postConstructWithTypeParamsSkipped() {
+               var bean = new TestPostConstructWithTypeParams();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               assertFalse(bean.called, "@PostConstruct method with type 
parameters should be skipped");
+       }
+
+       @Test
+       void b037_injectBeans_postConstructWithFieldAndMethodInjection() {
+               var service = new TestService("test1");
+               beanStore.addBean(TestService.class, service);
+               var bean = new TestPostConstruct();
+               ClassInfo.of(bean).inject(bean, beanStore);
+               // Verify all injection happened before PostConstruct
+               assertSame(service, bean.service, "Field injection should 
happen first");
+               assertSame(service, bean.injectedService, "PostConstruct should 
see injected field");
+               assertTrue(bean.postConstructCalled, "PostConstruct should be 
called after all injection");
+       }
 }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
index 0b78d9047d..5717b560a5 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/reflect/MethodInfo_Test.java
@@ -267,6 +267,23 @@ class MethodInfo_Test extends TestBase {
                assertSame(a_m, result);
        }
 
+       // Test classes for compareTo tie-breaker testing
+       public static class CompareToParent {
+               public CompareToParent getInstance() {
+                       return new CompareToParent();
+               }
+               public void method(String param) {}
+       }
+
+       public static class CompareToChild extends CompareToParent {
+               @Override
+               public CompareToChild getInstance() {
+                       return new CompareToChild();
+               }
+               @Override
+               public void method(String param) {}
+       }
+
        
//====================================================================================================
        // compareTo(MethodInfo)
        
//====================================================================================================
@@ -276,6 +293,42 @@ class MethodInfo_Test extends TestBase {
                check("[G.a1(), G.a1(int), G.a1(String), G.a1(int,int), G.a2(), 
G.a3()]", s);
        }
 
+       /**
+        * Tests that compareTo() correctly handles tie-breaking for overridden 
methods.
+        * When methods have the same name and parameters, child class methods 
should
+        * come before parent class methods in sorted order. This ensures 
consistent ordering
+        * and that methods with covariant return types are selected correctly.
+        */
+       @Test
+       void a002b_compareTo_tieBreakerForOverriddenMethods() throws Exception {
+               // Get methods from parent and child classes with same signature
+               var parentMethod = 
MethodInfo.of(CompareToParent.class.getMethod("getInstance"));
+               var childMethod = 
MethodInfo.of(CompareToChild.class.getMethod("getInstance"));
+
+               // Child method should come before parent method (negative 
comparison)
+               assertTrue(childMethod.compareTo(parentMethod) < 0, "Child 
method should come before parent method");
+               assertTrue(parentMethod.compareTo(childMethod) > 0, "Parent 
method should come after child method");
+
+               // When sorted, child should come first
+               var sorted = new TreeSet<>(l(parentMethod, childMethod));
+               var list = new ArrayList<>(sorted);
+               assertEquals(childMethod, list.get(0), "Child method should be 
first in sorted set");
+               assertEquals(parentMethod, list.get(1), "Parent method should 
be second in sorted set");
+
+               // Test with methods that have parameters
+               var parentMethodWithParam = 
MethodInfo.of(CompareToParent.class.getMethod("method", String.class));
+               var childMethodWithParam = 
MethodInfo.of(CompareToChild.class.getMethod("method", String.class));
+
+               
assertTrue(childMethodWithParam.compareTo(parentMethodWithParam) < 0, "Child 
method with params should come before parent");
+               
assertTrue(parentMethodWithParam.compareTo(childMethodWithParam) > 0, "Parent 
method with params should come after child");
+
+               // Test with unrelated classes (should fall back to class name 
comparison)
+               var unrelatedMethod = MethodInfo.of(A1.class.getMethod("m"));
+               var unrelatedMethod2 = 
MethodInfo.of(EqualsTestClass.class.getMethod("method1"));
+               // These should have different names, so comparison should be 
based on name, not tie-breaker
+               assertNotEquals(0, unrelatedMethod.compareTo(unrelatedMethod2));
+       }
+
        
//====================================================================================================
        // getAnnotatedReturnType()
        
//====================================================================================================
@@ -424,6 +477,49 @@ class MethodInfo_Test extends TestBase {
                check("Integer", d_a2.getReturnType());
        }
 
+       // Test classes for covariant return type testing
+       public static class ParentWithMethod {
+               public ParentWithMethod getInstance() {
+                       return new ParentWithMethod();
+               }
+       }
+
+       public static class ChildWithCovariantReturn extends ParentWithMethod {
+               @Override
+               public ChildWithCovariantReturn getInstance() {
+                       return new ChildWithCovariantReturn();
+               }
+       }
+
+       /**
+        * Tests that getReturnType() correctly handles covariant return types.
+        * When a child class overrides a parent method with a more specific 
return type,
+        * getReturnType() should return the child's return type, not the 
parent's.
+        */
+       @Test
+       void a013b_getReturnType_covariantReturnType() throws Exception {
+               // Get the method from the child class
+               var childMethod = 
MethodInfo.of(ChildWithCovariantReturn.class.getMethod("getInstance"));
+               var childReturnType = childMethod.getReturnType();
+
+               // Should return the child type, not the parent type
+               assertEquals(ChildWithCovariantReturn.class, 
childReturnType.inner());
+               assertNotEquals(ParentWithMethod.class, 
childReturnType.inner());
+
+               // Verify it's actually a child of the parent
+               
assertTrue(childReturnType.isChildOf(ClassInfo.of(ParentWithMethod.class)));
+
+               // Get the method from the parent class for comparison
+               var parentMethod = 
MethodInfo.of(ParentWithMethod.class.getMethod("getInstance"));
+               var parentReturnType = parentMethod.getReturnType();
+
+               // Parent method should return parent type
+               assertEquals(ParentWithMethod.class, parentReturnType.inner());
+
+               // Child and parent return types should be different
+               assertNotEquals(childReturnType.inner(), 
parentReturnType.inner());
+       }
+
        
//====================================================================================================
        // getSignature()
        
//====================================================================================================
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/Utils_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/Utils_Test.java
index e64db1fd06..47f72ecbbe 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/Utils_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/Utils_Test.java
@@ -20,6 +20,7 @@ import static 
org.apache.juneau.commons.utils.CollectionUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
 import static org.junit.jupiter.api.Assertions.*;
 
+import java.io.*;
 import java.lang.annotation.*;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -271,6 +272,54 @@ class Utils_Test extends TestBase {
                assertFalse(eq("not an annotation", a1));  // o1 is not, o2 is 
Annotation
        }
 
+       
//====================================================================================================
+       // eqAny(T, T...)
+       
//====================================================================================================
+       @Test
+       void a014_eqAny() {
+               // Basic equality tests
+               assertTrue(eqAny("apple", "apple", "banana", "cherry"));
+               assertFalse(eqAny("apple", "banana", "cherry"));
+               assertTrue(eqAny(123, 123, 456, 789));
+               assertFalse(eqAny(123, 456, 789));
+
+               // Null handling
+               assertTrue(eqAny(null, "apple", null, "banana"));
+               assertFalse(eqAny(null, "apple", "banana"));
+               assertTrue(eqAny("apple", null, "apple", "banana"));
+               assertFalse(eqAny("apple", "banana", "cherry"));
+
+               // Empty varargs
+               assertFalse(eqAny("apple"));
+               assertFalse(eqAny(null));
+               assertFalse(eqAny(123));
+
+               // Null varargs array
+               String[] nullArray = null;
+               assertFalse(eqAny("apple", nullArray));
+
+               // Test arrays
+               var arr1 = a(1, 2, 3);
+               var arr2 = a(1, 2, 3);
+               var arr3 = a(1, 2, 4);
+               assertTrue(eqAny(arr1, arr2, arr3));
+               assertFalse(eqAny(arr1, (Serializable[])arr3));
+
+               // Test annotations
+               @TestAnnotation("test")
+               class T1 {}
+               @TestAnnotation("test")
+               class T2 {}
+               @TestAnnotation("different")
+               class T3 {}
+
+               var a1 = T1.class.getAnnotation(TestAnnotation.class);
+               var a2 = T2.class.getAnnotation(TestAnnotation.class);
+               var a3 = T3.class.getAnnotation(TestAnnotation.class);
+               assertTrue(eqAny(a1, a2, a3));
+               assertFalse(eqAny(a1, a3));
+       }
+
        
//====================================================================================================
        // eq(T, U, BiPredicate<T,U>)
        
//====================================================================================================

Reply via email to