Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Release notes:

1.8.0 (not yet released)

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

1.7.1 (26-Sep-2025)

Expand Down
26 changes: 22 additions & 4 deletions src/main/java/com/fasterxml/classmate/TypeResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
76 changes: 76 additions & 0 deletions src/test/java/com/fasterxml/classmate/TestTypeResolver53.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.fasterxml.classmate;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import com.fasterxml.classmate.BaseTest;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;

// for [classmate#53]: Raw Comparator
public class TestTypeResolver53 extends BaseTest
{
@SuppressWarnings("rawtypes")
static abstract class Comparator53 implements Comparator { }

@SuppressWarnings("rawtypes")
static abstract class Map53 implements Map { }

@SuppressWarnings("rawtypes")
static abstract class BoundedComparable<T extends Number> implements Comparable { }

@SuppressWarnings("rawtypes")
static abstract class BoundedRaw extends BoundedComparable { }

static abstract class NestedRaw extends java.util.ArrayList<Map> { }

protected final TypeResolver RESOLVER = new TypeResolver();

// [classmate#53] Problem with Raw types - single type parameter
public void testResolvingRawType() {
ResolvedType rt = RESOLVER.resolve(Comparator53.class);
List<ResolvedType> params = rt.typeParametersFor(Comparator.class);
assertEquals(Arrays.asList(RESOLVER.resolve(Object.class)),
params);
}

// [classmate#53] Raw type with multiple type parameters (Map<K,V>)
public void testRawTypeMultipleParameters() {
ResolvedType rt = RESOLVER.resolve(Map53.class);
List<ResolvedType> params = rt.typeParametersFor(Map.class);
assertEquals(Arrays.asList(
RESOLVER.resolve(Object.class),
RESOLVER.resolve(Object.class)),
params);
}

// [classmate#53] Raw type with bounded type parameter
public void testRawTypeWithBoundedParameter() {
ResolvedType rt = RESOLVER.resolve(BoundedRaw.class);
List<ResolvedType> params = rt.typeParametersFor(Comparable.class);
// BoundedComparable<T extends Number> implements Comparable<T>, used raw
// Type parameter T has bound Number, so should resolve to Number
assertEquals(Arrays.asList(RESOLVER.resolve(Number.class)),
params);
}

// [classmate#53] Nested raw types (List<Map> where Map is raw)
public void testNestedRawType() {
ResolvedType rt = RESOLVER.resolve(NestedRaw.class);
List<ResolvedType> params = rt.typeParametersFor(java.util.ArrayList.class);
assertEquals(1, params.size());

// The type parameter should be Map (raw), which should have its own parameters
ResolvedType mapType = params.get(0);
assertEquals(Map.class, mapType.getErasedType());

// The raw Map should have Object,Object as its type parameters
List<ResolvedType> mapParams = mapType.getTypeParameters();
assertEquals(Arrays.asList(
RESOLVER.resolve(Object.class),
RESOLVER.resolve(Object.class)),
mapParams);
}
}

This file was deleted.