WildcardType to ParameterizedType

WildcardType to ParameterizedType



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.


List<? extends Number>



One of the candidate constructors is ArrayList(Collection<? extends E>)


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 ...


? 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:


new


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>.


resolvedParam


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:


com.google.common.reflect.Types


newParameterizedType()


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);






2 Answers
2



Converting wildcard type to ParameterizedType does not sound good here, but you can just implement that interface on your own.



Or use some other library that does support more dynamic creation:
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/TypeUtils.html

And probably many other libraries.



In guava you can only do something like that:


static <K, V> TypeToken<Map<K, V>> mapToken(TypeToken<K> keyToken, TypeToken<V> valueToken)
return new TypeToken<Map<K, V>>()
.where(new TypeParameter<K>() , keyToken)
.where(new TypeParameter<V>() , valueToken);

...
TypeToken<Map<String, BigInteger>> mapToken = mapToken(
TypeToken.of(String.class),
TypeToken.of(BigInteger.class));
TypeToken<Map<Integer, Queue<String>>> complexToken = mapToken(
TypeToken.of(Integer.class),
new TypeToken<Queue<String>>() );



It might be enough for you.






All good, but List<? extends SomeClass & SomeInterface> can't exist. Wildcards always have one upper bound.

– basin
Sep 9 '18 at 8:37


List<? extends SomeClass & SomeInterface>






You are right here, but if you are normalizing your generics to ParameterizedType type you might get type like this: <T extends SomeClass & SomeInterface> List<T> (as method return type, or T from class and as field) But yep, I forgot that they only added support for upper/lower bounds reflection API but not in language itself, but you still should think about case I mentioned above.

– GotoFinal
Sep 9 '18 at 8:49


<T extends SomeClass & SomeInterface> List<T>






How to rewrite mapToken() to accept an arbitrary raw class and an array of actual type arguments? There's no TypeParameter.of(Type) and TypeToken.of(Class) returns the raw type token, not a parameterized type needed for where()

– basin
Sep 11 '18 at 17:37


mapToken()


TypeParameter.of(Type)


TypeToken.of(Class)


where()






TypeToken.of(Object.class).getSubtype(Map.class) ?

– basin
Sep 11 '18 at 17:50


TypeToken.of(Object.class).getSubtype(Map.class)






I don't think it is possible with this library, at least I don't see any way, you can use some other one

– GotoFinal
Sep 13 '18 at 9:36




A custom newParameterizedType can be implemented with TypeResolver.where() (not to be confused with TypeToken.where()). The related part in my resolveConstructorResult() in the question can be changed like this:


newParameterizedType


TypeResolver.where()


TypeToken.where()


resolveConstructorResult()


if (doIt)
pt = (ParameterizedType)newParameterizedType(ptClazz, targs);
candidate = TypeToken.of(pt);

}
return candidate;
}

public static Type newParameterizedType(Class<?> cls, Type... args)
TypeResolver resolver = new TypeResolver();
TypeVariable<?> tvars = cls.getTypeParameters();
for (int i = 0; i < args.length; i++)
resolver = resolver.where(tvars[i], args[i]);

return resolver.resolveType(dumbToGenericType(cls).getType());


@SuppressWarnings("unchecked")
public static <T> TypeToken<? extends T> dumbToGenericType(Class<T> cls)
return (TypeToken<T>)TypeToken.of(Object.class).getSubtype(cls);



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)