I have the bean property of type List<? extends Number>
and the list of candidate implementation classes with their constructors. I recursively seek candidates for constructor parameters.
One of the candidate constructors is ArrayList(Collection<? extends E>)
I'm trying to resolve the type parameters and constructor parameters using Guava, but when there's a chain of wildcard types, I get something like: ? extends capture#3-of ? extends capture#2-of ? extends capture#1-of ? extends ...
public static <T extends Object> void main(String[] args) throws Exception {
TypeToken<?> propTt = new TypeToken<List<? extends Number>>() {
};
TypeToken<?> candidate = propTt.getSubtype(ArrayList.class);
TypeToken<?> constructorResult;
Constructor<?> cons = ArrayList.class.getConstructor(Collection.class);
// java.util.ArrayList<? extends java.lang.Number>
constructorResult = candidate.constructor(cons).getReturnType();
System.out.println(constructorResult);
// java.util.Collection<? extends E>
Type param = cons.getGenericParameterTypes()[0];
System.out.println(param);
// java.util.Collection<? extends capture#1-of ? extends class java.lang.Number>
TypeToken<?> resolvedParam = constructorResult.resolveType(param);
System.out.println(resolvedParam);
}
This happens although we can't use wildcards as type parameters for new
. The following is illegal:
List<?> x = new ArrayList<?>();
List<? extends Number> y = new ArrayList<? extends Number>();
Instead we write:
List<?> x = new ArrayList<Object>();
List<? extends Number> y = new ArrayList<Number>();
or automate the latter by using the diamond operator.
The desired type of resolvedParam
is java.util.Collection<? extends java.lang.Number>
.
com.google.common.reflect.Types
has the newParameterizedType()
method where I could pass the former wildcards manually resolved to parameterized types or classes, but this method is package-private. I'm uncertain my workaround:
package com.google.common.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
@SuppressWarnings("serial")
public class TestWildcard {
public static <T extends Object> void main(String[] args) throws Exception {
TypeToken<?> propTt = new TypeToken<List<? extends Number>>() {
};
TypeToken<?> candidate = propTt.getSubtype(ArrayList.class);
TypeToken<?> constructorResult;
Constructor<?> cons = ArrayList.class.getConstructor(Collection.class);
// new java.util.ArrayList<java.lang.Number>();
constructorResult = resolveConstructorResult(candidate);
System.out.println(constructorResult);
// java.util.Collection<? extends E>
Type param = cons.getGenericParameterTypes()[0];
System.out.println(param);
// java.util.Collection<? extends capture#1-of ? extends class java.lang.Number>
TypeToken<?> resolvedParam = constructorResult.resolveType(param);
System.out.println(resolvedParam);
}
static TypeToken<?> resolveConstructorResult(TypeToken<?> candidate) {
Type ct = candidate.getType();
if (ct instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) ct;
Class<?> ptClazz = ((Class<?>) pt.getRawType());
TypeVariable<?>[] tvars = ptClazz.getTypeParameters();
Type[] targs = pt.getActualTypeArguments();
boolean doIt = false;
for (int i = 0; i < targs.length; i++) {
if (targs[i] instanceof WildcardType) {
WildcardType wt = (WildcardType) targs[i];
TypeToken<?> ubound1 = TypeToken.of(wt.getUpperBounds()[0]);
TypeToken<?> ubound2 = TypeToken.of(tvars[i].getBounds()[0]);
if (ubound1.isSubtypeOf(ubound2)) {
doIt = true;
targs[i] = ubound1.getType();
} else if (ubound2.isSubtypeOf(ubound1)) {
doIt = true;
targs[i] = ubound2.getType();
}
}
}
if (doIt) {
pt = GuavaReflectAccessHelper.newParameterizedTypeWithOwner(pt.getOwnerType(), ptClazz, targs);
candidate = TypeToken.of(pt);
}
}
return candidate;
}
private static class GuavaReflectAccessHelper {
public static ParameterizedType newParameterizedTypeWithOwner(@Nullable Type ownerType, Class<?> rawType,
Type... arguments) {
return Types.newParameterizedTypeWithOwner(ownerType, rawType, arguments);
}
}
}
Aucun commentaire:
Enregistrer un commentaire