google / guava

Google core libraries for Java

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TypeVariable bounds should be considered when resolve types

d-william opened this issue · comments

Given :

public interface MyType<T> {}
public interface SubType<T> extends MyType<T> {}
public interface SubTypeInteger extends MyType<Integer> {}
public interface SubTypeBounded<T extends Number> extends MyType<T> {}
TypeToken<MyType<String>> type = new TypeToken<>() {};

When :

TypeToken<? extends MyType<String>> subType = type.getSubtype(SubType.class);

Then subType is SubType<String> (OK)

When :

TypeToken<? extends MyType<String>> subType = type.getSubtype(IntegerSubType.class);

Then java.lang.IllegalArgumentException: No type mapping from class java.lang.Integer to class java.lang.String is thrown (OK)

When :

TypeToken<? extends MyType<String>> subType = type.getSubtype(SubTypeBounded.class);

Then subType is SubTypeBounded<String> (KO?)
Should a java.lang.IllegalArgumentException be thrown ?
Maybe adding a check here would solve it?

@Override
void visitTypeVariable(TypeVariable<?> typeVariable) {
    Type[] bounds = typeVariable.getBounds();
    if (bounds.length > 0) {
        boolean contains = Arrays.stream(bounds)
                .map(TypeToken::of)
                .anyMatch(t -> t.getType() instanceof TypeVariable || t.isSupertypeOf(to)); //  t.getType() instanceof TypeVariable is too permisive ?
        checkArgument(!contains, "No type mapping from %s bounds (%s) to %s", typeVariable, Arrays.toString(bounds), to);
    }
    mappings.put(new TypeVariableKey(typeVariable), to);
}

I think @fluentfuture is indeed the expert here.

commented

Based on the code you provided, it seems that you are expecting the getSubtype method to throw a IllegalArgumentException when the specified subtype is not a valid subtype of the original TypeToken. However, it appears that this is not happening in the case where you are using the SubTypeBounded interface.

The reason for this behavior is that the SubTypeBounded interface is a valid subtype of the original TypeToken<MyType>, even though the type parameter T in SubTypeBounded is a bounded type parameter that extends Number. This is because the getSubtype method does not currently check the bounds of the type parameter when determining whether a given subtype is valid.

To fix this issue, you could add a check to the TypeToken.getSubtype method to verify that the bounds of the type parameter in the specified subtype are compatible with the original TypeToken. You could do this by adding a check similar to the one you proposed, which checks whether the bounds of the type parameter contain a TypeVariable or are a supertype of the original TypeToken. If the bounds are incompatible, you could throw a IllegalArgumentException to indicate that the subtype is not valid.

It's also worth noting that the TypeToken.getSubtype method is not designed to check for all possible subtype relationships, but rather to provide a convenient way to create new TypeToken instances that represent subtypes of the original TypeToken. If you need to perform more complex subtype checks, you may need to use other techniques or tools.