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
11 changes: 11 additions & 0 deletions src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,17 @@ private static boolean isAssignable(final Type type, final ParameterizedType toP
}
// get the target type's type arguments including owner type arguments
final Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType, toClass, typeVarAssigns);
// Class<T> is not assignable to Class<S> if T is not S (even if T extends S)
if (toClass.equals(Class.class)) {
final TypeVariable<?>[] typeParams = toClass.getTypeParameters();
if (typeParams.length > 0) {
final Type toTypeArg = unrollVariableAssignments(typeParams[0], toTypeVarAssigns);
final Type fromTypeArg = unrollVariableAssignments(typeParams[0], fromTypeVarAssigns);
if (toTypeArg != null && (fromTypeArg == null || !toTypeArg.equals(fromTypeArg))) {
return false;
}
}
}
// now to check each type argument
for (final TypeVariable<?> var : toTypeVarAssigns.keySet()) {
final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
Expand Down
27 changes: 27 additions & 0 deletions src/test/java/org/apache/commons/lang3/reflect/TypeUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,33 @@ void testIsAssignableGenericArrayTypeToWildcardType() {
() -> String.format("TypeUtils.isAssignable(%s, %s)", testType, paramType));
}

@Test
void testIsAssignable_ClassWithParameterizedType() {
final ParameterizedType topre1 = TypeUtils.parameterize(TestIF.class, TypeUtils.wildcardType().build());
final Type to1 = TypeUtils.parameterize(Class.class, TypeUtils.wildcardType().withUpperBounds(topre1).build());
final Type from1 = TypeUtils.parameterize(Class.class, TestIF.class);
assertFalse(TypeUtils.isAssignable(from1, to1), "Class<TestIF> should not be assignable to Class<? extends TestIF<?>>");

final ParameterizedType topre2 = TypeUtils.parameterize(TestIF.class, TypeUtils.wildcardType().build());
final Type to2 = TypeUtils.parameterize(Class.class, TypeUtils.wildcardType().withUpperBounds(topre2).build());
final Type from2 = TypeUtils.parameterize(Class.class, TestImpl.class);
assertFalse(TypeUtils.isAssignable(from2, to2), "Class<TestImpl> should not be assignable to Class<? extends TestIF<?>>");

final ParameterizedType topre3 = TypeUtils.parameterize(TestIF.class, Number.class);
final Type to3 = TypeUtils.parameterize(Class.class, TypeUtils.wildcardType().withUpperBounds(topre3).build());
final Type from3 = TypeUtils.parameterize(Class.class, TestImpl2.class);
assertFalse(TypeUtils.isAssignable(from3, to3), "Class<TestImpl2> should not be assignable to Class<? extends TestIF<Number>>");
}

private interface TestIF<T> {
}

private static class TestImpl<T> implements TestIF<T> {
}

private static class TestImpl2<R> implements TestIF<Number> {
}

@Test
void testIsAssignableGenericClassHierarchy() throws NoSuchFieldException {
/*
Expand Down
Loading