Support non-generic type for `TypeToken.getParameterized` for legacy reasons (#2447)

This partially restores the behavior before a589ef2008,
except that back then for a non-generic type a bogus `TypeToken(ParameterizedType)`
was created, whereas now a `TypeToken(Class)` is created instead.
This commit is contained in:
Marcono1234 2023-07-25 22:00:26 +02:00 committed by GitHub
parent 79ae239a49
commit a38b757bc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 14 deletions

View File

@ -337,9 +337,12 @@ public class TypeToken<T> {
* As seen here the result is a {@code TypeToken<?>}; this method cannot provide any type safety,
* and care must be taken to pass in the correct number of type arguments.
*
* <p>If {@code rawType} is a non-generic class and no type arguments are provided, this method
* simply delegates to {@link #get(Class)} and creates a {@code TypeToken(Class)}.
*
* @throws IllegalArgumentException
* If {@code rawType} is not of type {@code Class}, if it is not a generic type, or if the
* type arguments are invalid for the raw type
* If {@code rawType} is not of type {@code Class}, or if the type arguments are invalid for
* the raw type
*/
public static TypeToken<?> getParameterized(Type rawType, Type... typeArguments) {
Objects.requireNonNull(rawType);
@ -354,10 +357,16 @@ public class TypeToken<T> {
Class<?> rawClass = (Class<?>) rawType;
TypeVariable<?>[] typeVariables = rawClass.getTypeParameters();
// Note: Does not check if owner type of rawType is generic because this factory method
// does not support specifying owner type
if (typeVariables.length == 0) {
throw new IllegalArgumentException(rawClass.getName() + " is not a generic type");
int expectedArgsCount = typeVariables.length;
int actualArgsCount = typeArguments.length;
if (actualArgsCount != expectedArgsCount) {
throw new IllegalArgumentException(rawClass.getName() + " requires " + expectedArgsCount +
" type arguments, but got " + actualArgsCount);
}
// For legacy reasons create a TypeToken(Class) if the type is not generic
if (typeArguments.length == 0) {
return get(rawClass);
}
// Check for this here to avoid misleading exception thrown by ParameterizedTypeImpl
@ -366,13 +375,6 @@ public class TypeToken<T> {
+ " it requires specifying an owner type");
}
int expectedArgsCount = typeVariables.length;
int actualArgsCount = typeArguments.length;
if (actualArgsCount != expectedArgsCount) {
throw new IllegalArgumentException(rawClass.getName() + " requires " + expectedArgsCount +
" type arguments, but got " + actualArgsCount);
}
for (int i = 0; i < expectedArgsCount; i++) {
Type typeArgument = Objects.requireNonNull(typeArguments[i], "Type argument must not be null");
Class<?> rawTypeArgument = $Gson$Types.getRawType(typeArgument);

View File

@ -146,6 +146,9 @@ public final class TypeTokenTest {
class LocalGenericClass<T> {}
TypeToken<?> expectedLocalType = new TypeToken<LocalGenericClass<Integer>>() {};
assertThat(TypeToken.getParameterized(LocalGenericClass.class, Integer.class)).isEqualTo(expectedLocalType);
// For legacy reasons, if requesting parameterized type for non-generic class, create a `TypeToken(Class)`
assertThat(TypeToken.getParameterized(String.class)).isEqualTo(TypeToken.get(String.class));
}
@Test
@ -158,7 +161,7 @@ public final class TypeTokenTest {
assertThat(e).hasMessageThat().isEqualTo("rawType must be of type Class, but was java.lang.String[]");
e = assertThrows(IllegalArgumentException.class, () -> TypeToken.getParameterized(String.class, Number.class));
assertThat(e).hasMessageThat().isEqualTo("java.lang.String is not a generic type");
assertThat(e).hasMessageThat().isEqualTo("java.lang.String requires 0 type arguments, but got 1");
e = assertThrows(IllegalArgumentException.class, () -> TypeToken.getParameterized(List.class, new Type[0]));
assertThat(e).hasMessageThat().isEqualTo("java.util.List requires 1 type arguments, but got 0");