New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolve method type parameters using argument types #3771
Comments
Maybe something like this?
|
I get Type resolvedParameterType =
new TypeResolver()
.where(
parameterType.getType(),
argumentType.getSupertype((Class) parameterType.getRawType()).getType())
.resolveType(parameterType.getType());
System.out.println(resolvedParameterType);
// List<String>
Type resolvedReturnType =
new TypeResolver()
.where(
parameterType.getType(),
argumentType.getSupertype((Class) parameterType.getRawType()).getType())
.resolveType(returnType.getType());
System.out.println(resolvedReturnType);
// List<String> Thanks! I forgot that |
There are some issues with this approach, which I've tried to illustrate in this gist: https://gist.github.com/michaelhixson/bd159355c8ebf454882fb9672c84bb92 The issues make me think Is the gist working as intended (and so there really is no API for solving this problem), or is it highlighting bugs in |
Yeah. Wildcard is likely a problem. For example in |
The following patch to diff --git a/guava/src/com/google/common/reflect/TypeResolver.java b/guava/src/com/google/common/reflect/TypeResolver.java
index 339eb43b5..cecbc376e 100644
--- a/guava/src/com/google/common/reflect/TypeResolver.java
+++ b/guava/src/com/google/common/reflect/TypeResolver.java
@@ -30,6 +30,7 @@ import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
@@ -135,7 +136,17 @@ public final class TypeResolver {
@Override
void visitWildcardType(WildcardType fromWildcardType) {
if (!(to instanceof WildcardType)) {
- return; // okay to say <?> is anything
+ for (Type fromUpperBound : fromWildcardType.getUpperBounds()) {
+ if (!(fromUpperBound instanceof Class)) {
+ populateTypeMappings(mappings, fromUpperBound, to);
+ }
+ }
+ for (Type fromLowerBound : fromWildcardType.getLowerBounds()) {
+ if (!(fromLowerBound instanceof Class)) {
+ populateTypeMappings(mappings, fromLowerBound, to);
+ }
+ }
+ return;
}
WildcardType toWildcardType = (WildcardType) to;
Type[] fromUpperBounds = fromWildcardType.getUpperBounds();
@@ -161,6 +172,10 @@ public final class TypeResolver {
if (to instanceof WildcardType) {
return; // Okay to say Foo<A> is <?>
}
+ if (to instanceof Class && fromParameterizedType.getRawType().equals(Class.class)) {
+ populateTypeMappings(mappings, fromParameterizedType.getActualTypeArguments()[0], to);
+ return;
+ }
ParameterizedType toParameterizedType = expectArgument(ParameterizedType.class, to);
if (fromParameterizedType.getOwnerType() != null
&& toParameterizedType.getOwnerType() != null) {
@@ -168,7 +183,7 @@ public final class TypeResolver {
mappings, fromParameterizedType.getOwnerType(), toParameterizedType.getOwnerType());
}
checkArgument(
- fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()),
+ ((Class<?>) fromParameterizedType.getRawType()).isAssignableFrom((Class<?>) toParameterizedType.getRawType()),
"Inconsistent raw type: %s vs. %s",
fromParameterizedType,
to);
@@ -287,15 +302,31 @@ public final class TypeResolver {
/** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */
final TypeTable where(Map<TypeVariableKey, ? extends Type> mappings) {
- ImmutableMap.Builder<TypeVariableKey, Type> builder = ImmutableMap.builder();
- builder.putAll(map);
+ Map<TypeVariableKey, Type> builder = new LinkedHashMap<>(map);
for (Entry<TypeVariableKey, ? extends Type> mapping : mappings.entrySet()) {
TypeVariableKey variable = mapping.getKey();
Type type = mapping.getValue();
checkArgument(!variable.equalsType(type), "Type variable %s bound to itself", variable);
- builder.put(variable, type);
+ builder.merge(
+ variable,
+ type,
+ (oldType, newType) -> {
+ if (TypeToken.of(newType).isSubtypeOf(oldType)) {
+ return newType;
+ }
+ if (TypeToken.of(oldType).isSubtypeOf(newType)) {
+ return oldType;
+ }
+ throw new IllegalArgumentException(
+ "Incompatible types for "
+ + variable
+ + ": "
+ + oldType.getTypeName()
+ + " and "
+ + newType.getTypeName());
+ });
}
- return new TypeTable(builder.build());
+ return new TypeTable(ImmutableMap.copyOf(builder));
}
final Type resolve(final TypeVariable<?> var) { It causes |
I recall there was a lot of complexities in dealing with wildcard inference. For example, how does one resolve But then if the method has another What if the args' compile-time types also involve wildcard? TypeResolver so far mostly assumes invariance which works for cases when you want to know what But when covariance or contra-variance is needed (such as at call-site), the type deduction rules seem very complicated to get right all the time. And particularly, |
Well, I'm diving in! master...michaelhixson:type-resolver-fixes Currently it knows that this method static <T> T example(List<? extends T> a, List<? extends T> b) { return null; } with arguments
Haven't tried this one yet. Also haven't tried methods that use both class-level type variables and method-level type variables. I'm going to keep working on this for a bit. |
Add some API for resolving the type parameters of methods, in particular when some or all of the argument types for a method invocation are known.
TypeToken#resolveType(Type)
is useful for resolving type parameters of classes, but I don't see a way to resolve type parameters of methods.Take the method
Lists#reverse(List)
for example. Suppose I have obtained the following somehow.java.lang.reflect.Method
object representingLists#reverse(List)
. (I just have a reference to aMethod
. I don't have static knowledge that it's thereverse
method specifically.)Type
of the argument I'd like to provide to this method, such asArrayList<String>
.Type
of the variable in which I'd like to store the result returned from this method, such asCollection<String>
.and I want to answer these questions.
I don't think there is any API for answering either of those questions right now. This code doesn't get there.
I'm not sure what such an API would look like. Here's one idea.
The text was updated successfully, but these errors were encountered: