seenTypes,
}
// Finally, only include first instance of an interface, so:
ClassKey key = new ClassKey(currentType.getErasedType());
- if (seenTypes.contains(key)) {
+ if (!seenTypes.add(key)) {
return;
}
// If all good so far, append
- seenTypes.add(key);
types.add(currentType);
/* and check supertypes; starting with interfaces. Why interfaces?
* So that "highest" interfaces get priority; otherwise we'd recurse
diff --git a/src/main/java/com/fasterxml/classmate/TypeResolver.java b/src/main/java/com/fasterxml/classmate/TypeResolver.java
index a143036..b37ecab 100644
--- a/src/main/java/com/fasterxml/classmate/TypeResolver.java
+++ b/src/main/java/com/fasterxml/classmate/TypeResolver.java
@@ -13,7 +13,7 @@
/**
* Object that is used for resolving generic type information of a class
* so that it is accessible using simple API. Resolved types are also starting
- * point for accessing resolved (generics aware) return and argument types
+ * point for accessing resolved (generics-aware) return and argument types
* of class members (methods, fields, constructors).
*
* Note that resolver instances are stateful in that resolvers cache resolved
@@ -422,10 +422,28 @@ private ResolvedType _constructType(ClassStack context, Class> rawType, TypeBi
ResolvedType elementType = _fromAny(context, rawType.getComponentType(), typeBindings);
return new ResolvedArrayType(rawType, typeBindings, elementType);
}
- // Work-around/fix for [#33]: if the type has no type parameters, don't include
- // typeBindings in the ResolvedType
- if (!typeBindings.isEmpty() && rawType.getTypeParameters().length == 0) {
- typeBindings = TypeBindings.emptyBindings();
+ final TypeVariable>[] rawTypeParameters = rawType.getTypeParameters();
+ // [classmate#53]: Handle raw generic types - resolve type parameters to their bounds
+ if (typeBindings.isEmpty()) {
+ if (rawTypeParameters.length > 0) {
+ ResolvedType[] types = new ResolvedType[rawTypeParameters.length];
+ for (int i = 0; i < rawTypeParameters.length; ++i) {
+ // Resolve each type parameter to its bound (similar to _fromVariable)
+ TypeVariable> var = rawTypeParameters[i];
+ String name = var.getName();
+ // Avoid self-reference cycles by marking as unbound during resolution
+ TypeBindings tempBindings = typeBindings.withUnboundVariable(name);
+ Type[] bounds = var.getBounds();
+ types[i] = _fromAny(context, bounds[0], tempBindings);
+ }
+ typeBindings = TypeBindings.create(rawType, types);
+ }
+ } else {
+ // Work-around/fix for [classmate#33]: if the type has no type parameters,
+ // don't include typeBindings in the ResolvedType
+ if (rawTypeParameters.length == 0) {
+ typeBindings = TypeBindings.emptyBindings();
+ }
}
// For other types super interfaces are needed...
if (rawType.isInterface()) {
diff --git a/src/main/java/com/fasterxml/classmate/types/ResolvedArrayType.java b/src/main/java/com/fasterxml/classmate/types/ResolvedArrayType.java
index 4630f3a..b7c81d8 100644
--- a/src/main/java/com/fasterxml/classmate/types/ResolvedArrayType.java
+++ b/src/main/java/com/fasterxml/classmate/types/ResolvedArrayType.java
@@ -7,8 +7,21 @@
public final class ResolvedArrayType extends ResolvedType
{
- protected final ResolvedType _elementType;
+ /**
+ * All Java Arrays extend {@link java.lang.Object} so we need
+ * a reference
+ *
+ * Note that direct construction is used instead of construction via
+ * {@link TypeResolver} due to complexity of doing latter: {@code java.lang.Object}
+ * also does not implement any interfaces so this should be safe enough.
+ *
+ * @since 1.7
+ */
+ private final static ResolvedObjectType PARENT_TYPE =
+ ResolvedObjectType.create(Object.class, null, null, null);
+ protected final ResolvedType _elementType;
+
/*
/**********************************************************************
/* Life cycle
@@ -34,7 +47,7 @@ public boolean canCreateSubtypes() {
*/
@Override
- public ResolvedType getParentClass() { return null; }
+ public ResolvedType getParentClass() { return PARENT_TYPE; }
@Override
public ResolvedType getSelfReferencedType() { return null; }
@@ -69,7 +82,7 @@ public boolean isInterface() {
/*
/**********************************************************************
- /* Accessors for raw (minimally procesed) members
+ /* Accessors for raw (minimally processed) members
/**********************************************************************
*/
diff --git a/src/main/java/com/fasterxml/classmate/types/ResolvedObjectType.java b/src/main/java/com/fasterxml/classmate/types/ResolvedObjectType.java
index 5b54221..9b354a8 100644
--- a/src/main/java/com/fasterxml/classmate/types/ResolvedObjectType.java
+++ b/src/main/java/com/fasterxml/classmate/types/ResolvedObjectType.java
@@ -83,20 +83,6 @@ public ResolvedObjectType(Class> erased, TypeBindings bindings,
_modifiers = erased.getModifiers();
}
- @Deprecated // since 1.1; removed from 1.2 -- kept for binary backwards compatibility
- public ResolvedObjectType(Class> erased, TypeBindings bindings,
- ResolvedObjectType superClass, List interfaces)
- {
- this(erased, bindings, (ResolvedType) superClass, interfaces);
- }
-
- @Deprecated // since 1.1; removed from 1.2 -- kept for binary backwards compatibility
- public ResolvedObjectType(Class> erased, TypeBindings bindings,
- ResolvedObjectType superClass, ResolvedType[] interfaces)
- {
- this(erased, bindings, (ResolvedType) superClass, interfaces);
- }
-
public static ResolvedObjectType create(Class> erased, TypeBindings bindings,
ResolvedType superClass, List interfaces)
{
diff --git a/src/main/java/com/fasterxml/classmate/types/ResolvedRecursiveType.java b/src/main/java/com/fasterxml/classmate/types/ResolvedRecursiveType.java
index f43143e..efa9e7d 100644
--- a/src/main/java/com/fasterxml/classmate/types/ResolvedRecursiveType.java
+++ b/src/main/java/com/fasterxml/classmate/types/ResolvedRecursiveType.java
@@ -151,23 +151,21 @@ public StringBuilder appendFullDescription(StringBuilder sb)
/* Other overrides
/**********************************************************************
*/
-
- @Override public boolean equals(Object o)
+
+ // 02-Jan-2026: [classmate#117]: Do NOT compare _referencedType to avoid infinite
+ // recursion: super.equals() already compares class type, erased type, and type
+ // bindings, which is sufficient for determining equality of recursive types.
+ // Comparing _referencedType causes StackOverflowError when comparing types
+ // from different TypeResolver instances.
+ /*
+ @Override
+ public boolean equals(Object o)
{
- if (!super.equals(o)) {
- return false;
- }
- // not sure if we should match at all, but definitely need this
- // additional part if we do:
- ResolvedRecursiveType other = (ResolvedRecursiveType) o;
- if (_referencedType == null) {
- return other._referencedType == null;
- }
- return _referencedType.equals(other._referencedType);
+ return super.equals(o);
}
+ */
// Only for compliance purposes: lgtm.com complains if only equals overridden
- @Override public int hashCode() {
- return super.hashCode();
- }
+ // 02-Jan-2026, tatu: No longer, base impl is fine
+ // @Override public int hashCode() { return super.hashCode(); }
}
diff --git a/src/test/java/com/fasterxml/classmate/AnnotationsTest.java b/src/test/java/com/fasterxml/classmate/AnnotationsTest.java
index 096bed3..6b0a49e 100644
--- a/src/test/java/com/fasterxml/classmate/AnnotationsTest.java
+++ b/src/test/java/com/fasterxml/classmate/AnnotationsTest.java
@@ -7,17 +7,15 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
-import static junit.framework.Assert.*;
-import static org.junit.Assert.assertEquals;
-
-@SuppressWarnings("deprecation")
-public class AnnotationsTest {
+import static org.junit.Assert.*;
+public class AnnotationsTest
+{
@Retention(RetentionPolicy.RUNTIME)
private static @interface Marker { }
@Test @Marker
- public void addAsDefault() throws NoSuchMethodException {
+ public void addAsDefault() throws Exception {
Annotations annotations = new Annotations();
Method thisMethod = AnnotationsTest.class.getDeclaredMethod("addAsDefault");
@@ -39,7 +37,7 @@ public void addAsDefault() throws NoSuchMethodException {
}
@Test
- public void size() throws NoSuchMethodException {
+ public void size() throws Exception {
Annotations annotations = new Annotations();
Method thisMethod = AnnotationsTest.class.getDeclaredMethod("addAsDefault");
@@ -57,7 +55,7 @@ public void size() throws NoSuchMethodException {
}
@Test
- public void annotationsToSize() throws NoSuchMethodException {
+ public void annotationsToSize() throws Exception {
Annotations annotations = new Annotations();
Method thisMethod = AnnotationsTest.class.getDeclaredMethod("addAsDefault");
@@ -67,23 +65,42 @@ public void annotationsToSize() throws NoSuchMethodException {
annotations.addAsDefault(testAnnotation);
// order is unspecified as the internal representation is a HashMap; just assert the constituent parts are present
- String asString = annotations.toString();
+ String asString = _normalize(annotations.toString());
assertTrue(asString.contains("{interface org.junit.Test=@org.junit.Test("));
assertTrue(asString.contains("timeout=0"));
// 15-Nov-2016, tatu: Java 9 changes description slightly, need to modify
- assertTrue(asString.contains("expected=class org.junit.Test$None") // until Java 8
- || asString.contains("expected=org.junit.Test$None"));
+ // 05-Dec-2025, tatu: Java 21 adds further variation
+ if (!(asString.contains("expected=class org.junit.Test.None") // until Java 8
+ || asString.contains("expected=org.junit.Test.None"))) {
+ fail("No 'expected' in: "+asString);
+ }
Annotation markerAnnotation = thisMethod.getAnnotation(Marker.class);
annotations.addAsDefault(markerAnnotation);
- asString = annotations.toString();
- assertTrue(asString.contains("interface com.fasterxml.classmate.AnnotationsTest$Marker=@com.fasterxml.classmate.AnnotationsTest$Marker()"));
+ asString = _normalize(annotations.toString());
+
+ String exp = "interface com.fasterxml.classmate.AnnotationsTest.Marker=@com.fasterxml.classmate.AnnotationsTest.Marker()";
+ if (!asString.contains(exp)) {
+ fail("Expected: ["+exp+"]\nin ["+asString+"]");
+ }
assertTrue(asString.contains("interface org.junit.Test=@org.junit.Test"));
assertTrue(asString.contains("timeout=0"));
// 15-Nov-2016, tatu: Java 9 changes description slightly, need to modify
- assertTrue(asString.contains("expected=class org.junit.Test$None")
- || asString.contains("expected=org.junit.Test$None"));
+ // 05-Dec-2025, tatu: Java 21 adds further variation
+ if (!(asString.contains("expected=class org.junit.Test.None") // until Java 8
+ || asString.contains("expected=org.junit.Test.None"))) {
+ fail("No 'expected' in: "+asString);
+ }
+ }
+
+ private static String _normalize(String str) {
+ // 05-Dec-2025, tatu: Java 21 changes from "org.junit.Test$None" to "org.junit.Test.None"
+ String str2;
+ while ((str2 = str.replace('$', '.')) != str) {
+ str = str2;
+ }
+ return str;
}
}
diff --git a/src/test/java/com/fasterxml/classmate/TestMemberResolver.java b/src/test/java/com/fasterxml/classmate/MemberResolverTest.java
similarity index 92%
rename from src/test/java/com/fasterxml/classmate/TestMemberResolver.java
rename to src/test/java/com/fasterxml/classmate/MemberResolverTest.java
index 38965b1..00db56c 100644
--- a/src/test/java/com/fasterxml/classmate/TestMemberResolver.java
+++ b/src/test/java/com/fasterxml/classmate/MemberResolverTest.java
@@ -1,6 +1,5 @@
package com.fasterxml.classmate;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
@@ -8,7 +7,7 @@
import com.fasterxml.classmate.types.ResolvedObjectType;
import com.fasterxml.classmate.util.ClassKey;
-public class TestMemberResolver extends BaseTest
+public class MemberResolverTest extends BaseTest
{
/*
/**********************************************************************
@@ -214,7 +213,11 @@ public void testIncludeObject()
mr.setIncludeLangObject(true);
simpleResolvedTypeWithMembers = mr.resolve(simpleResolvedType, null, null);
- assertEquals(12, simpleResolvedTypeWithMembers.getMemberMethods().length);
+ // With JDK < 21, 12 member methods, 21+ 13
+ int methodCount = simpleResolvedTypeWithMembers.getMemberMethods().length;
+ if (methodCount < 12 || methodCount > 13) {
+ fail("Expected [12, 13] methods, got: "+methodCount);
+ }
assertEquals(1, simpleResolvedTypeWithMembers.getMemberFields().length);
}
@@ -225,7 +228,12 @@ public void testFilters()
mr.setIncludeLangObject(true);
ResolvedTypeWithMembers simpleResolvedTypeWithMembers = mr.resolve(simpleResolvedType, null, null);
- assertEquals(12, simpleResolvedTypeWithMembers.getMemberMethods().length);
+ // With JDK < 21, 12 member methods, 21+ 13
+ int methodCount = simpleResolvedTypeWithMembers.getMemberMethods().length;
+ if (methodCount < 12 || methodCount > 13) {
+ fail("Expected [12, 13] methods, got: "+methodCount);
+ }
+
assertEquals(1, simpleResolvedTypeWithMembers.getMemberFields().length);
assertEquals(2, simpleResolvedTypeWithMembers.getConstructors().length);
@@ -253,7 +261,12 @@ public void testFilters()
});
simpleResolvedTypeWithMembers = mr.resolve(simpleResolvedType, null, null);
- assertEquals(12, simpleResolvedTypeWithMembers.getMemberMethods().length);
+
+ // With JDK < 21, 12 member methods, 21+ 13
+ methodCount = simpleResolvedTypeWithMembers.getMemberMethods().length;
+ if (methodCount < 12 || methodCount > 13) {
+ fail("Expected [12, 13] methods, got: "+methodCount);
+ }
assertEquals(0, simpleResolvedTypeWithMembers.getMemberFields().length);
assertEquals(2, simpleResolvedTypeWithMembers.getConstructors().length);
@@ -267,11 +280,19 @@ public void testFilters()
});
simpleResolvedTypeWithMembers = mr.resolve(simpleResolvedType, null, null);
- assertEquals(12, simpleResolvedTypeWithMembers.getMemberMethods().length);
+ // With JDK < 21, 12 member methods, 21+ 13
+ methodCount = simpleResolvedTypeWithMembers.getMemberMethods().length;
+ if (methodCount < 12 || methodCount > 13) {
+ fail("Expected [12, 13] methods, got: "+methodCount);
+ }
assertEquals(1, simpleResolvedTypeWithMembers.getMemberFields().length);
assertEquals(1, simpleResolvedTypeWithMembers.getConstructors().length);
}
+ // 10-Oct-2023, tatu: Why, what, how? I don't think internals should be tested
+ // like this. And since this breaks with JDK 17 (probably due to additional
+ // interfaces in JDK types), will comment out.
+/*
public void testAddOverridesFromInterfaces() throws IllegalAccessException, InvocationTargetException
{
ResolvedType resolvedType = typeResolver.resolve(MemberResolver.class);
@@ -317,6 +338,7 @@ public void testAddOverridesFromInterfaces() throws IllegalAccessException, Invo
assertEquals(2, seenTypes.size());
assertEquals(2, typesWithOverrides.size());
}
+ */
public void testGatherTypesWithInterfaces() throws Exception
{
diff --git a/src/test/java/com/fasterxml/classmate/ResolvedTypeTest.java b/src/test/java/com/fasterxml/classmate/ResolvedTypeTest.java
index 07666bc..ae60492 100644
--- a/src/test/java/com/fasterxml/classmate/ResolvedTypeTest.java
+++ b/src/test/java/com/fasterxml/classmate/ResolvedTypeTest.java
@@ -12,7 +12,7 @@
public class ResolvedTypeTest extends BaseTest
{
- // For [Issue#16]
+ // For [classmate#16]
static class Foo16 extends Bar16 { }
@@ -35,11 +35,10 @@ public void testCanCreateSubtype() {
assertFalse(arrayType.canCreateSubtype(String[].class));
assertFalse(arrayType.canCreateSubtype(CharBuffer[].class));
assertFalse(arrayType.canCreateSubtype(String.class));
-
}
@Test
- public void testtypeParametersFor() {
+ public void testTypeParametersFor() {
ResolvedObjectType stringType = ResolvedObjectType.create(String.class, null, null, null);
assertNull(stringType.typeParametersFor(CharBuffer.class));
}
@@ -113,7 +112,7 @@ public void testAccessors() {
assertEquals(0, type.getStaticFields().size());
}
- // For [Issue#16]
+ // For [classmate#16]
@Test
public void testIssue16()
{
diff --git a/src/test/java/com/fasterxml/classmate/ResolvedTypeWithMembersTest.java b/src/test/java/com/fasterxml/classmate/ResolvedTypeWithMembersTest.java
index 8fb5b96..dfeffde 100644
--- a/src/test/java/com/fasterxml/classmate/ResolvedTypeWithMembersTest.java
+++ b/src/test/java/com/fasterxml/classmate/ResolvedTypeWithMembersTest.java
@@ -16,9 +16,8 @@
/**
* User: blangel
*/
-@SuppressWarnings("deprecation")
-public class ResolvedTypeWithMembersTest {
-
+public class ResolvedTypeWithMembersTest
+{
@Retention(RetentionPolicy.RUNTIME)
static @interface Marker { }
diff --git a/src/test/java/com/fasterxml/classmate/StdConfigurationTest.java b/src/test/java/com/fasterxml/classmate/StdConfigurationTest.java
index 62323cd..bb6a08c 100644
--- a/src/test/java/com/fasterxml/classmate/StdConfigurationTest.java
+++ b/src/test/java/com/fasterxml/classmate/StdConfigurationTest.java
@@ -5,9 +5,8 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertSame;
-@SuppressWarnings("deprecation")
-public class StdConfigurationTest {
-
+public class StdConfigurationTest
+{
@Test
public void getInclusionForClass() {
AnnotationConfiguration.StdConfiguration config = new AnnotationConfiguration.StdConfiguration(null);
diff --git a/src/test/java/com/fasterxml/classmate/TestReadme.java b/src/test/java/com/fasterxml/classmate/TestReadme.java
index 01a4df5..55d113d 100644
--- a/src/test/java/com/fasterxml/classmate/TestReadme.java
+++ b/src/test/java/com/fasterxml/classmate/TestReadme.java
@@ -39,7 +39,6 @@ public static class SomeOtherClass {
public void someMethod() { }
}
- @Test @SuppressWarnings("serial")
public void resolvingClasses() {
TypeResolver typeResolver = new TypeResolver();
ResolvedType listType = typeResolver.resolve(List.class);
diff --git a/src/test/java/com/fasterxml/classmate/TestTypeDescriptions.java b/src/test/java/com/fasterxml/classmate/TestTypeDescriptions.java
index 567e107..ae224ef 100644
--- a/src/test/java/com/fasterxml/classmate/TestTypeDescriptions.java
+++ b/src/test/java/com/fasterxml/classmate/TestTypeDescriptions.java
@@ -32,12 +32,24 @@ public void testSimpleTypes()
assertEquals("Ljava/lang/Object;", objectType.getSignature());
ResolvedType stringType = typeResolver.resolve(String.class);
- /* Interesting thing with "simple" type like java.lang.String is that
- * it has recursive type self-reference (via Comparable)
- */
- assertEquals("java.lang.String extends java.lang.Object"
- +" implements java.io.Serializable,java.lang.Comparable,java.lang.CharSequence",
- stringType.getFullDescription());
+ // Interesting thing with "simple" type like java.lang.String is that
+ // it has recursive type self-reference (via Comparable)
+
+ final String stringDesc = stringType.getFullDescription();
+
+ // 10-Oct-2023, tatu: With JDK 17, get even more stuff... Start with pre-17 desc:
+ if (stringDesc.equals("java.lang.String extends java.lang.Object"
+ +" implements java.io.Serializable,java.lang.Comparable,java.lang.CharSequence"
+ )) {
+ ;
+ // But then allow JDK 17 variant
+ } else if (stringDesc.equals("java.lang.String extends java.lang.Object"
+ +" implements java.io.Serializable,java.lang.Comparable,java.lang.CharSequence"
+ +",java.lang.constant.Constable,java.lang.constant.ConstantDesc"
+ )) {
+ } else {
+ fail("Full String description not matching one of expected signatures: "+stringDesc);
+ }
assertEquals("Ljava/lang/String;", stringType.getErasedSignature());
assertEquals("Ljava/lang/String;", stringType.getSignature());
}
@@ -56,7 +68,6 @@ public void testPrimitiveTypes()
assertEquals("boolean", boolType.getFullDescription());
}
- @SuppressWarnings("serial")
public void testGenericTypes()
{
ResolvedType mapType = typeResolver.resolve(new GenericType