Skip to content

Commit 57fb93a

Browse files
authored
Fix #53 (#113)
1 parent 5ea96fa commit 57fb93a

File tree

4 files changed

+100
-31
lines changed

4 files changed

+100
-31
lines changed

VERSION.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ Release notes:
55

66
1.8.0 (not yet released)
77

8-
-
8+
#53: Allow for subtype resolution with unknown generics
9+
(reported by Carsten W, @CarstenWickner)
910

1011
1.7.1 (26-Sep-2025)
1112

src/main/java/com/fasterxml/classmate/TypeResolver.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,28 @@ private ResolvedType _constructType(ClassStack context, Class<?> rawType, TypeBi
422422
ResolvedType elementType = _fromAny(context, rawType.getComponentType(), typeBindings);
423423
return new ResolvedArrayType(rawType, typeBindings, elementType);
424424
}
425-
// Work-around/fix for [#33]: if the type has no type parameters, don't include
426-
// typeBindings in the ResolvedType
427-
if (!typeBindings.isEmpty() && rawType.getTypeParameters().length == 0) {
428-
typeBindings = TypeBindings.emptyBindings();
425+
final TypeVariable<?>[] rawTypeParameters = rawType.getTypeParameters();
426+
// [classmate#53]: Handle raw generic types - resolve type parameters to their bounds
427+
if (typeBindings.isEmpty()) {
428+
if (rawTypeParameters.length > 0) {
429+
ResolvedType[] types = new ResolvedType[rawTypeParameters.length];
430+
for (int i = 0; i < rawTypeParameters.length; ++i) {
431+
// Resolve each type parameter to its bound (similar to _fromVariable)
432+
TypeVariable<?> var = rawTypeParameters[i];
433+
String name = var.getName();
434+
// Avoid self-reference cycles by marking as unbound during resolution
435+
TypeBindings tempBindings = typeBindings.withUnboundVariable(name);
436+
Type[] bounds = var.getBounds();
437+
types[i] = _fromAny(context, bounds[0], tempBindings);
438+
}
439+
typeBindings = TypeBindings.create(rawType, types);
440+
}
441+
} else {
442+
// Work-around/fix for [classmate#33]: if the type has no type parameters,
443+
// don't include typeBindings in the ResolvedType
444+
if (rawTypeParameters.length == 0) {
445+
typeBindings = TypeBindings.emptyBindings();
446+
}
429447
}
430448
// For other types super interfaces are needed...
431449
if (rawType.isInterface()) {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.fasterxml.classmate;
2+
3+
import java.util.Arrays;
4+
import java.util.Comparator;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
import com.fasterxml.classmate.BaseTest;
9+
import com.fasterxml.classmate.ResolvedType;
10+
import com.fasterxml.classmate.TypeResolver;
11+
12+
// for [classmate#53]: Raw Comparator
13+
public class TestTypeResolver53 extends BaseTest
14+
{
15+
@SuppressWarnings("rawtypes")
16+
static abstract class Comparator53 implements Comparator { }
17+
18+
@SuppressWarnings("rawtypes")
19+
static abstract class Map53 implements Map { }
20+
21+
@SuppressWarnings("rawtypes")
22+
static abstract class BoundedComparable<T extends Number> implements Comparable { }
23+
24+
@SuppressWarnings("rawtypes")
25+
static abstract class BoundedRaw extends BoundedComparable { }
26+
27+
static abstract class NestedRaw extends java.util.ArrayList<Map> { }
28+
29+
protected final TypeResolver RESOLVER = new TypeResolver();
30+
31+
// [classmate#53] Problem with Raw types - single type parameter
32+
public void testResolvingRawType() {
33+
ResolvedType rt = RESOLVER.resolve(Comparator53.class);
34+
List<ResolvedType> params = rt.typeParametersFor(Comparator.class);
35+
assertEquals(Arrays.asList(RESOLVER.resolve(Object.class)),
36+
params);
37+
}
38+
39+
// [classmate#53] Raw type with multiple type parameters (Map<K,V>)
40+
public void testRawTypeMultipleParameters() {
41+
ResolvedType rt = RESOLVER.resolve(Map53.class);
42+
List<ResolvedType> params = rt.typeParametersFor(Map.class);
43+
assertEquals(Arrays.asList(
44+
RESOLVER.resolve(Object.class),
45+
RESOLVER.resolve(Object.class)),
46+
params);
47+
}
48+
49+
// [classmate#53] Raw type with bounded type parameter
50+
public void testRawTypeWithBoundedParameter() {
51+
ResolvedType rt = RESOLVER.resolve(BoundedRaw.class);
52+
List<ResolvedType> params = rt.typeParametersFor(Comparable.class);
53+
// BoundedComparable<T extends Number> implements Comparable<T>, used raw
54+
// Type parameter T has bound Number, so should resolve to Number
55+
assertEquals(Arrays.asList(RESOLVER.resolve(Number.class)),
56+
params);
57+
}
58+
59+
// [classmate#53] Nested raw types (List<Map> where Map is raw)
60+
public void testNestedRawType() {
61+
ResolvedType rt = RESOLVER.resolve(NestedRaw.class);
62+
List<ResolvedType> params = rt.typeParametersFor(java.util.ArrayList.class);
63+
assertEquals(1, params.size());
64+
65+
// The type parameter should be Map (raw), which should have its own parameters
66+
ResolvedType mapType = params.get(0);
67+
assertEquals(Map.class, mapType.getErasedType());
68+
69+
// The raw Map should have Object,Object as its type parameters
70+
List<ResolvedType> mapParams = mapType.getTypeParameters();
71+
assertEquals(Arrays.asList(
72+
RESOLVER.resolve(Object.class),
73+
RESOLVER.resolve(Object.class)),
74+
mapParams);
75+
}
76+
}

src/test/java/com/fasterxml/classmate/failing/TestTypeResolver53.java

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)