William Degrange created LANG-1836:
--------------------------------------
Summary: TypeUtils.containsTypeVariables(Type) ignores the owner
type of a ParameterizedType
Key: LANG-1836
URL: https://issues.apache.org/jira/browse/LANG-1836
Project: Commons Lang
Issue Type: Bug
Affects Versions: 3.21.0
Reporter: William Degrange
*Description*
{{TypeUtils.containsTypeVariables(Type)}} returns {{false}} for a
{{ParameterizedType}} whose owner type contains a type variable. The
{{ParameterizedType}} branch only checks {{{}getActualTypeArguments(){}}};
{{getOwnerType()}} is never inspected.
This contradicts the Javadoc ("Tests, recursively, whether any of the type
parameters associated with {{type}} are bound to variables"): the type
parameters of the owner are part of the type's parameterization, and
{{unrollVariables}} already handles the owner explicitly.
*Steps to reproduce*
{code:java}
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import org.apache.commons.lang3.reflect.TypeUtils;public class
ContainsTypeVariablesOwnerRepro { static class Outer<T> {
class Inner {}
Inner inner; // generic type is Outer<T>.Inner
} public static void main(String[] args) throws Exception {
Type fromReflection =
Outer.class.getDeclaredField("inner").getGenericType();
System.out.println(TypeUtils.containsTypeVariables(fromReflection)); //
false, expected true TypeVariable<?> t =
Outer.class.getTypeParameters()[0];
Type built =
TypeUtils.parameterizeWithOwner(TypeUtils.parameterize(Outer.class, t),
Outer.Inner.class);
System.out.println(TypeUtils.containsTypeVariables(built)); // false,
expected true // Consequence: unrollVariables short-circuits and does
not substitute T
System.out.println(TypeUtils.unrollVariables(Collections.singletonMap(t,
String.class), fromReflection));
// prints Outer<T>.Inner, expected Outer<String>.Inner
}
}{code}
*Actual:* {{{}false{}}}, {{{}false{}}}, {{Outer<T>.Inner}}
*Expected:* {{{}true{}}}, {{{}true{}}}, {{Outer<String>.Inner}}
*Suggested fix*
Recurse into the owner type, but only when it is not a raw {{{}Class{}}}. For a
static nested type such as {{{}Map.Entry<String, Integer>{}}}, the owner is the
raw {{{}Map.class{}}}, and the {{Class}} branch would return {{true}} because
{{Map}} declares type parameters:
{code:java}
if (type instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
for (final Type arg : parameterizedType.getActualTypeArguments()) {
if (containsTypeVariables(arg)) {
return true;
}
}
final Type ownerType = parameterizedType.getOwnerType();
return ownerType != null && !(ownerType instanceof Class<?>) &&
containsTypeVariables(ownerType);
} {code}
Suggested regression tests:
* {{Outer<T>.Inner}} → {{true}}
* {{Outer<String>.Inner}} → {{false}}
* {{Map.Entry<String, Integer>}} → {{false}} (the owner is the raw
{{{}Map.class{}}})
--
This message was sent by Atlassian Jira
(v8.20.10#820010)